#!/usr/bin/env python3
# The MIT License (MIT)
#
# Copyright (c) 2014 Richard Moore
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

# This is a pure-Python implementation of the AES algorithm and AES common
# modes of operation.

# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard

# Honestly, the best description of the modes of operations are the wonderful
# diagrams on Wikipedia. They explain in moments what my words could never
# achieve. Hence the inline documentation here is sparer than I'd prefer.
# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation

# Also useful, PyCrypto, a crypto library implemented in C with Python bindings:
# https://www.dlitz.net/software/pycrypto/


# Supported key sizes:
#   128-bit
#   192-bit
#   256-bit


# Supported modes of operation:
#   ECB - Electronic Codebook
#   CBC - Cipher-Block Chaining
#   CFB - Cipher Feedback
#   OFB - Output Feedback
#   CTR - Counter


# See the README.md for API details and general information.


import copy
import struct

__all__ = [
  "AES",
  "AESModeOfOperationCTR",
  "AESModeOfOperationCBC",
  "AESModeOfOperationCFB",
  "AESModeOfOperationECB",
  "AESModeOfOperationOFB",
  "AESModesOfOperation",
  "Counter",
]


def _compact_word(word):
  return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3]


def _string_to_bytes(text):
  return list(ord(c) for c in text)


def _bytes_to_string(binary):
  return "".join(chr(b) for b in binary)


def _concat_list(a, b):
  return a + b


# Python 3 compatibility
try:
  xrange
except Exception:
  xrange = range

  # Python 3 supports bytes, which is already an array of integers
  def _string_to_bytes(text):
    if isinstance(text, bytes):
      return text
    return [ord(c) for c in text]

  # In Python 3, we return bytes
  def _bytes_to_string(binary):
    return bytes(binary)

  # Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first
  def _concat_list(a, b):
    return a + bytes(b)


# Based *largely* on the Rijndael implementation
# See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
class AES(object):
  """Encapsulates the AES block cipher.

  You generally should not need this. Use the AESModeOfOperation classes
  below instead."""

  # Number of rounds by keysize
  number_of_rounds = {16: 10, 24: 12, 32: 14}

  # Round constant words
  rcon = [
    0x01,
    0x02,
    0x04,
    0x08,
    0x10,
    0x20,
    0x40,
    0x80,
    0x1B,
    0x36,
    0x6C,
    0xD8,
    0xAB,
    0x4D,
    0x9A,
    0x2F,
    0x5E,
    0xBC,
    0x63,
    0xC6,
    0x97,
    0x35,
    0x6A,
    0xD4,
    0xB3,
    0x7D,
    0xFA,
    0xEF,
    0xC5,
    0x91,
  ]

  # S-box and Inverse S-box (S is for Substitution)
  S = [
    0x63,
    0x7C,
    0x77,
    0x7B,
    0xF2,
    0x6B,
    0x6F,
    0xC5,
    0x30,
    0x01,
    0x67,
    0x2B,
    0xFE,
    0xD7,
    0xAB,
    0x76,
    0xCA,
    0x82,
    0xC9,
    0x7D,
    0xFA,
    0x59,
    0x47,
    0xF0,
    0xAD,
    0xD4,
    0xA2,
    0xAF,
    0x9C,
    0xA4,
    0x72,
    0xC0,
    0xB7,
    0xFD,
    0x93,
    0x26,
    0x36,
    0x3F,
    0xF7,
    0xCC,
    0x34,
    0xA5,
    0xE5,
    0xF1,
    0x71,
    0xD8,
    0x31,
    0x15,
    0x04,
    0xC7,
    0x23,
    0xC3,
    0x18,
    0x96,
    0x05,
    0x9A,
    0x07,
    0x12,
    0x80,
    0xE2,
    0xEB,
    0x27,
    0xB2,
    0x75,
    0x09,
    0x83,
    0x2C,
    0x1A,
    0x1B,
    0x6E,
    0x5A,
    0xA0,
    0x52,
    0x3B,
    0xD6,
    0xB3,
    0x29,
    0xE3,
    0x2F,
    0x84,
    0x53,
    0xD1,
    0x00,
    0xED,
    0x20,
    0xFC,
    0xB1,
    0x5B,
    0x6A,
    0xCB,
    0xBE,
    0x39,
    0x4A,
    0x4C,
    0x58,
    0xCF,
    0xD0,
    0xEF,
    0xAA,
    0xFB,
    0x43,
    0x4D,
    0x33,
    0x85,
    0x45,
    0xF9,
    0x02,
    0x7F,
    0x50,
    0x3C,
    0x9F,
    0xA8,
    0x51,
    0xA3,
    0x40,
    0x8F,
    0x92,
    0x9D,
    0x38,
    0xF5,
    0xBC,
    0xB6,
    0xDA,
    0x21,
    0x10,
    0xFF,
    0xF3,
    0xD2,
    0xCD,
    0x0C,
    0x13,
    0xEC,
    0x5F,
    0x97,
    0x44,
    0x17,
    0xC4,
    0xA7,
    0x7E,
    0x3D,
    0x64,
    0x5D,
    0x19,
    0x73,
    0x60,
    0x81,
    0x4F,
    0xDC,
    0x22,
    0x2A,
    0x90,
    0x88,
    0x46,
    0xEE,
    0xB8,
    0x14,
    0xDE,
    0x5E,
    0x0B,
    0xDB,
    0xE0,
    0x32,
    0x3A,
    0x0A,
    0x49,
    0x06,
    0x24,
    0x5C,
    0xC2,
    0xD3,
    0xAC,
    0x62,
    0x91,
    0x95,
    0xE4,
    0x79,
    0xE7,
    0xC8,
    0x37,
    0x6D,
    0x8D,
    0xD5,
    0x4E,
    0xA9,
    0x6C,
    0x56,
    0xF4,
    0xEA,
    0x65,
    0x7A,
    0xAE,
    0x08,
    0xBA,
    0x78,
    0x25,
    0x2E,
    0x1C,
    0xA6,
    0xB4,
    0xC6,
    0xE8,
    0xDD,
    0x74,
    0x1F,
    0x4B,
    0xBD,
    0x8B,
    0x8A,
    0x70,
    0x3E,
    0xB5,
    0x66,
    0x48,
    0x03,
    0xF6,
    0x0E,
    0x61,
    0x35,
    0x57,
    0xB9,
    0x86,
    0xC1,
    0x1D,
    0x9E,
    0xE1,
    0xF8,
    0x98,
    0x11,
    0x69,
    0xD9,
    0x8E,
    0x94,
    0x9B,
    0x1E,
    0x87,
    0xE9,
    0xCE,
    0x55,
    0x28,
    0xDF,
    0x8C,
    0xA1,
    0x89,
    0x0D,
    0xBF,
    0xE6,
    0x42,
    0x68,
    0x41,
    0x99,
    0x2D,
    0x0F,
    0xB0,
    0x54,
    0xBB,
    0x16,
  ]
  Si = [
    0x52,
    0x09,
    0x6A,
    0xD5,
    0x30,
    0x36,
    0xA5,
    0x38,
    0xBF,
    0x40,
    0xA3,
    0x9E,
    0x81,
    0xF3,
    0xD7,
    0xFB,
    0x7C,
    0xE3,
    0x39,
    0x82,
    0x9B,
    0x2F,
    0xFF,
    0x87,
    0x34,
    0x8E,
    0x43,
    0x44,
    0xC4,
    0xDE,
    0xE9,
    0xCB,
    0x54,
    0x7B,
    0x94,
    0x32,
    0xA6,
    0xC2,
    0x23,
    0x3D,
    0xEE,
    0x4C,
    0x95,
    0x0B,
    0x42,
    0xFA,
    0xC3,
    0x4E,
    0x08,
    0x2E,
    0xA1,
    0x66,
    0x28,
    0xD9,
    0x24,
    0xB2,
    0x76,
    0x5B,
    0xA2,
    0x49,
    0x6D,
    0x8B,
    0xD1,
    0x25,
    0x72,
    0xF8,
    0xF6,
    0x64,
    0x86,
    0x68,
    0x98,
    0x16,
    0xD4,
    0xA4,
    0x5C,
    0xCC,
    0x5D,
    0x65,
    0xB6,
    0x92,
    0x6C,
    0x70,
    0x48,
    0x50,
    0xFD,
    0xED,
    0xB9,
    0xDA,
    0x5E,
    0x15,
    0x46,
    0x57,
    0xA7,
    0x8D,
    0x9D,
    0x84,
    0x90,
    0xD8,
    0xAB,
    0x00,
    0x8C,
    0xBC,
    0xD3,
    0x0A,
    0xF7,
    0xE4,
    0x58,
    0x05,
    0xB8,
    0xB3,
    0x45,
    0x06,
    0xD0,
    0x2C,
    0x1E,
    0x8F,
    0xCA,
    0x3F,
    0x0F,
    0x02,
    0xC1,
    0xAF,
    0xBD,
    0x03,
    0x01,
    0x13,
    0x8A,
    0x6B,
    0x3A,
    0x91,
    0x11,
    0x41,
    0x4F,
    0x67,
    0xDC,
    0xEA,
    0x97,
    0xF2,
    0xCF,
    0xCE,
    0xF0,
    0xB4,
    0xE6,
    0x73,
    0x96,
    0xAC,
    0x74,
    0x22,
    0xE7,
    0xAD,
    0x35,
    0x85,
    0xE2,
    0xF9,
    0x37,
    0xE8,
    0x1C,
    0x75,
    0xDF,
    0x6E,
    0x47,
    0xF1,
    0x1A,
    0x71,
    0x1D,
    0x29,
    0xC5,
    0x89,
    0x6F,
    0xB7,
    0x62,
    0x0E,
    0xAA,
    0x18,
    0xBE,
    0x1B,
    0xFC,
    0x56,
    0x3E,
    0x4B,
    0xC6,
    0xD2,
    0x79,
    0x20,
    0x9A,
    0xDB,
    0xC0,
    0xFE,
    0x78,
    0xCD,
    0x5A,
    0xF4,
    0x1F,
    0xDD,
    0xA8,
    0x33,
    0x88,
    0x07,
    0xC7,
    0x31,
    0xB1,
    0x12,
    0x10,
    0x59,
    0x27,
    0x80,
    0xEC,
    0x5F,
    0x60,
    0x51,
    0x7F,
    0xA9,
    0x19,
    0xB5,
    0x4A,
    0x0D,
    0x2D,
    0xE5,
    0x7A,
    0x9F,
    0x93,
    0xC9,
    0x9C,
    0xEF,
    0xA0,
    0xE0,
    0x3B,
    0x4D,
    0xAE,
    0x2A,
    0xF5,
    0xB0,
    0xC8,
    0xEB,
    0xBB,
    0x3C,
    0x83,
    0x53,
    0x99,
    0x61,
    0x17,
    0x2B,
    0x04,
    0x7E,
    0xBA,
    0x77,
    0xD6,
    0x26,
    0xE1,
    0x69,
    0x14,
    0x63,
    0x55,
    0x21,
    0x0C,
    0x7D,
  ]

  # Transformations for encryption
  T1 = [
    0xC66363A5,
    0xF87C7C84,
    0xEE777799,
    0xF67B7B8D,
    0xFFF2F20D,
    0xD66B6BBD,
    0xDE6F6FB1,
    0x91C5C554,
    0x60303050,
    0x02010103,
    0xCE6767A9,
    0x562B2B7D,
    0xE7FEFE19,
    0xB5D7D762,
    0x4DABABE6,
    0xEC76769A,
    0x8FCACA45,
    0x1F82829D,
    0x89C9C940,
    0xFA7D7D87,
    0xEFFAFA15,
    0xB25959EB,
    0x8E4747C9,
    0xFBF0F00B,
    0x41ADADEC,
    0xB3D4D467,
    0x5FA2A2FD,
    0x45AFAFEA,
    0x239C9CBF,
    0x53A4A4F7,
    0xE4727296,
    0x9BC0C05B,
    0x75B7B7C2,
    0xE1FDFD1C,
    0x3D9393AE,
    0x4C26266A,
    0x6C36365A,
    0x7E3F3F41,
    0xF5F7F702,
    0x83CCCC4F,
    0x6834345C,
    0x51A5A5F4,
    0xD1E5E534,
    0xF9F1F108,
    0xE2717193,
    0xABD8D873,
    0x62313153,
    0x2A15153F,
    0x0804040C,
    0x95C7C752,
    0x46232365,
    0x9DC3C35E,
    0x30181828,
    0x379696A1,
    0x0A05050F,
    0x2F9A9AB5,
    0x0E070709,
    0x24121236,
    0x1B80809B,
    0xDFE2E23D,
    0xCDEBEB26,
    0x4E272769,
    0x7FB2B2CD,
    0xEA75759F,
    0x1209091B,
    0x1D83839E,
    0x582C2C74,
    0x341A1A2E,
    0x361B1B2D,
    0xDC6E6EB2,
    0xB45A5AEE,
    0x5BA0A0FB,
    0xA45252F6,
    0x763B3B4D,
    0xB7D6D661,
    0x7DB3B3CE,
    0x5229297B,
    0xDDE3E33E,
    0x5E2F2F71,
    0x13848497,
    0xA65353F5,
    0xB9D1D168,
    0x00000000,
    0xC1EDED2C,
    0x40202060,
    0xE3FCFC1F,
    0x79B1B1C8,
    0xB65B5BED,
    0xD46A6ABE,
    0x8DCBCB46,
    0x67BEBED9,
    0x7239394B,
    0x944A4ADE,
    0x984C4CD4,
    0xB05858E8,
    0x85CFCF4A,
    0xBBD0D06B,
    0xC5EFEF2A,
    0x4FAAAAE5,
    0xEDFBFB16,
    0x864343C5,
    0x9A4D4DD7,
    0x66333355,
    0x11858594,
    0x8A4545CF,
    0xE9F9F910,
    0x04020206,
    0xFE7F7F81,
    0xA05050F0,
    0x783C3C44,
    0x259F9FBA,
    0x4BA8A8E3,
    0xA25151F3,
    0x5DA3A3FE,
    0x804040C0,
    0x058F8F8A,
    0x3F9292AD,
    0x219D9DBC,
    0x70383848,
    0xF1F5F504,
    0x63BCBCDF,
    0x77B6B6C1,
    0xAFDADA75,
    0x42212163,
    0x20101030,
    0xE5FFFF1A,
    0xFDF3F30E,
    0xBFD2D26D,
    0x81CDCD4C,
    0x180C0C14,
    0x26131335,
    0xC3ECEC2F,
    0xBE5F5FE1,
    0x359797A2,
    0x884444CC,
    0x2E171739,
    0x93C4C457,
    0x55A7A7F2,
    0xFC7E7E82,
    0x7A3D3D47,
    0xC86464AC,
    0xBA5D5DE7,
    0x3219192B,
    0xE6737395,
    0xC06060A0,
    0x19818198,
    0x9E4F4FD1,
    0xA3DCDC7F,
    0x44222266,
    0x542A2A7E,
    0x3B9090AB,
    0x0B888883,
    0x8C4646CA,
    0xC7EEEE29,
    0x6BB8B8D3,
    0x2814143C,
    0xA7DEDE79,
    0xBC5E5EE2,
    0x160B0B1D,
    0xADDBDB76,
    0xDBE0E03B,
    0x64323256,
    0x743A3A4E,
    0x140A0A1E,
    0x924949DB,
    0x0C06060A,
    0x4824246C,
    0xB85C5CE4,
    0x9FC2C25D,
    0xBDD3D36E,
    0x43ACACEF,
    0xC46262A6,
    0x399191A8,
    0x319595A4,
    0xD3E4E437,
    0xF279798B,
    0xD5E7E732,
    0x8BC8C843,
    0x6E373759,
    0xDA6D6DB7,
    0x018D8D8C,
    0xB1D5D564,
    0x9C4E4ED2,
    0x49A9A9E0,
    0xD86C6CB4,
    0xAC5656FA,
    0xF3F4F407,
    0xCFEAEA25,
    0xCA6565AF,
    0xF47A7A8E,
    0x47AEAEE9,
    0x10080818,
    0x6FBABAD5,
    0xF0787888,
    0x4A25256F,
    0x5C2E2E72,
    0x381C1C24,
    0x57A6A6F1,
    0x73B4B4C7,
    0x97C6C651,
    0xCBE8E823,
    0xA1DDDD7C,
    0xE874749C,
    0x3E1F1F21,
    0x964B4BDD,
    0x61BDBDDC,
    0x0D8B8B86,
    0x0F8A8A85,
    0xE0707090,
    0x7C3E3E42,
    0x71B5B5C4,
    0xCC6666AA,
    0x904848D8,
    0x06030305,
    0xF7F6F601,
    0x1C0E0E12,
    0xC26161A3,
    0x6A35355F,
    0xAE5757F9,
    0x69B9B9D0,
    0x17868691,
    0x99C1C158,
    0x3A1D1D27,
    0x279E9EB9,
    0xD9E1E138,
    0xEBF8F813,
    0x2B9898B3,
    0x22111133,
    0xD26969BB,
    0xA9D9D970,
    0x078E8E89,
    0x339494A7,
    0x2D9B9BB6,
    0x3C1E1E22,
    0x15878792,
    0xC9E9E920,
    0x87CECE49,
    0xAA5555FF,
    0x50282878,
    0xA5DFDF7A,
    0x038C8C8F,
    0x59A1A1F8,
    0x09898980,
    0x1A0D0D17,
    0x65BFBFDA,
    0xD7E6E631,
    0x844242C6,
    0xD06868B8,
    0x824141C3,
    0x299999B0,
    0x5A2D2D77,
    0x1E0F0F11,
    0x7BB0B0CB,
    0xA85454FC,
    0x6DBBBBD6,
    0x2C16163A,
  ]
  T2 = [
    0xA5C66363,
    0x84F87C7C,
    0x99EE7777,
    0x8DF67B7B,
    0x0DFFF2F2,
    0xBDD66B6B,
    0xB1DE6F6F,
    0x5491C5C5,
    0x50603030,
    0x03020101,
    0xA9CE6767,
    0x7D562B2B,
    0x19E7FEFE,
    0x62B5D7D7,
    0xE64DABAB,
    0x9AEC7676,
    0x458FCACA,
    0x9D1F8282,
    0x4089C9C9,
    0x87FA7D7D,
    0x15EFFAFA,
    0xEBB25959,
    0xC98E4747,
    0x0BFBF0F0,
    0xEC41ADAD,
    0x67B3D4D4,
    0xFD5FA2A2,
    0xEA45AFAF,
    0xBF239C9C,
    0xF753A4A4,
    0x96E47272,
    0x5B9BC0C0,
    0xC275B7B7,
    0x1CE1FDFD,
    0xAE3D9393,
    0x6A4C2626,
    0x5A6C3636,
    0x417E3F3F,
    0x02F5F7F7,
    0x4F83CCCC,
    0x5C683434,
    0xF451A5A5,
    0x34D1E5E5,
    0x08F9F1F1,
    0x93E27171,
    0x73ABD8D8,
    0x53623131,
    0x3F2A1515,
    0x0C080404,
    0x5295C7C7,
    0x65462323,
    0x5E9DC3C3,
    0x28301818,
    0xA1379696,
    0x0F0A0505,
    0xB52F9A9A,
    0x090E0707,
    0x36241212,
    0x9B1B8080,
    0x3DDFE2E2,
    0x26CDEBEB,
    0x694E2727,
    0xCD7FB2B2,
    0x9FEA7575,
    0x1B120909,
    0x9E1D8383,
    0x74582C2C,
    0x2E341A1A,
    0x2D361B1B,
    0xB2DC6E6E,
    0xEEB45A5A,
    0xFB5BA0A0,
    0xF6A45252,
    0x4D763B3B,
    0x61B7D6D6,
    0xCE7DB3B3,
    0x7B522929,
    0x3EDDE3E3,
    0x715E2F2F,
    0x97138484,
    0xF5A65353,
    0x68B9D1D1,
    0x00000000,
    0x2CC1EDED,
    0x60402020,
    0x1FE3FCFC,
    0xC879B1B1,
    0xEDB65B5B,
    0xBED46A6A,
    0x468DCBCB,
    0xD967BEBE,
    0x4B723939,
    0xDE944A4A,
    0xD4984C4C,
    0xE8B05858,
    0x4A85CFCF,
    0x6BBBD0D0,
    0x2AC5EFEF,
    0xE54FAAAA,
    0x16EDFBFB,
    0xC5864343,
    0xD79A4D4D,
    0x55663333,
    0x94118585,
    0xCF8A4545,
    0x10E9F9F9,
    0x06040202,
    0x81FE7F7F,
    0xF0A05050,
    0x44783C3C,
    0xBA259F9F,
    0xE34BA8A8,
    0xF3A25151,
    0xFE5DA3A3,
    0xC0804040,
    0x8A058F8F,
    0xAD3F9292,
    0xBC219D9D,
    0x48703838,
    0x04F1F5F5,
    0xDF63BCBC,
    0xC177B6B6,
    0x75AFDADA,
    0x63422121,
    0x30201010,
    0x1AE5FFFF,
    0x0EFDF3F3,
    0x6DBFD2D2,
    0x4C81CDCD,
    0x14180C0C,
    0x35261313,
    0x2FC3ECEC,
    0xE1BE5F5F,
    0xA2359797,
    0xCC884444,
    0x392E1717,
    0x5793C4C4,
    0xF255A7A7,
    0x82FC7E7E,
    0x477A3D3D,
    0xACC86464,
    0xE7BA5D5D,
    0x2B321919,
    0x95E67373,
    0xA0C06060,
    0x98198181,
    0xD19E4F4F,
    0x7FA3DCDC,
    0x66442222,
    0x7E542A2A,
    0xAB3B9090,
    0x830B8888,
    0xCA8C4646,
    0x29C7EEEE,
    0xD36BB8B8,
    0x3C281414,
    0x79A7DEDE,
    0xE2BC5E5E,
    0x1D160B0B,
    0x76ADDBDB,
    0x3BDBE0E0,
    0x56643232,
    0x4E743A3A,
    0x1E140A0A,
    0xDB924949,
    0x0A0C0606,
    0x6C482424,
    0xE4B85C5C,
    0x5D9FC2C2,
    0x6EBDD3D3,
    0xEF43ACAC,
    0xA6C46262,
    0xA8399191,
    0xA4319595,
    0x37D3E4E4,
    0x8BF27979,
    0x32D5E7E7,
    0x438BC8C8,
    0x596E3737,
    0xB7DA6D6D,
    0x8C018D8D,
    0x64B1D5D5,
    0xD29C4E4E,
    0xE049A9A9,
    0xB4D86C6C,
    0xFAAC5656,
    0x07F3F4F4,
    0x25CFEAEA,
    0xAFCA6565,
    0x8EF47A7A,
    0xE947AEAE,
    0x18100808,
    0xD56FBABA,
    0x88F07878,
    0x6F4A2525,
    0x725C2E2E,
    0x24381C1C,
    0xF157A6A6,
    0xC773B4B4,
    0x5197C6C6,
    0x23CBE8E8,
    0x7CA1DDDD,
    0x9CE87474,
    0x213E1F1F,
    0xDD964B4B,
    0xDC61BDBD,
    0x860D8B8B,
    0x850F8A8A,
    0x90E07070,
    0x427C3E3E,
    0xC471B5B5,
    0xAACC6666,
    0xD8904848,
    0x05060303,
    0x01F7F6F6,
    0x121C0E0E,
    0xA3C26161,
    0x5F6A3535,
    0xF9AE5757,
    0xD069B9B9,
    0x91178686,
    0x5899C1C1,
    0x273A1D1D,
    0xB9279E9E,
    0x38D9E1E1,
    0x13EBF8F8,
    0xB32B9898,
    0x33221111,
    0xBBD26969,
    0x70A9D9D9,
    0x89078E8E,
    0xA7339494,
    0xB62D9B9B,
    0x223C1E1E,
    0x92158787,
    0x20C9E9E9,
    0x4987CECE,
    0xFFAA5555,
    0x78502828,
    0x7AA5DFDF,
    0x8F038C8C,
    0xF859A1A1,
    0x80098989,
    0x171A0D0D,
    0xDA65BFBF,
    0x31D7E6E6,
    0xC6844242,
    0xB8D06868,
    0xC3824141,
    0xB0299999,
    0x775A2D2D,
    0x111E0F0F,
    0xCB7BB0B0,
    0xFCA85454,
    0xD66DBBBB,
    0x3A2C1616,
  ]
  T3 = [
    0x63A5C663,
    0x7C84F87C,
    0x7799EE77,
    0x7B8DF67B,
    0xF20DFFF2,
    0x6BBDD66B,
    0x6FB1DE6F,
    0xC55491C5,
    0x30506030,
    0x01030201,
    0x67A9CE67,
    0x2B7D562B,
    0xFE19E7FE,
    0xD762B5D7,
    0xABE64DAB,
    0x769AEC76,
    0xCA458FCA,
    0x829D1F82,
    0xC94089C9,
    0x7D87FA7D,
    0xFA15EFFA,
    0x59EBB259,
    0x47C98E47,
    0xF00BFBF0,
    0xADEC41AD,
    0xD467B3D4,
    0xA2FD5FA2,
    0xAFEA45AF,
    0x9CBF239C,
    0xA4F753A4,
    0x7296E472,
    0xC05B9BC0,
    0xB7C275B7,
    0xFD1CE1FD,
    0x93AE3D93,
    0x266A4C26,
    0x365A6C36,
    0x3F417E3F,
    0xF702F5F7,
    0xCC4F83CC,
    0x345C6834,
    0xA5F451A5,
    0xE534D1E5,
    0xF108F9F1,
    0x7193E271,
    0xD873ABD8,
    0x31536231,
    0x153F2A15,
    0x040C0804,
    0xC75295C7,
    0x23654623,
    0xC35E9DC3,
    0x18283018,
    0x96A13796,
    0x050F0A05,
    0x9AB52F9A,
    0x07090E07,
    0x12362412,
    0x809B1B80,
    0xE23DDFE2,
    0xEB26CDEB,
    0x27694E27,
    0xB2CD7FB2,
    0x759FEA75,
    0x091B1209,
    0x839E1D83,
    0x2C74582C,
    0x1A2E341A,
    0x1B2D361B,
    0x6EB2DC6E,
    0x5AEEB45A,
    0xA0FB5BA0,
    0x52F6A452,
    0x3B4D763B,
    0xD661B7D6,
    0xB3CE7DB3,
    0x297B5229,
    0xE33EDDE3,
    0x2F715E2F,
    0x84971384,
    0x53F5A653,
    0xD168B9D1,
    0x00000000,
    0xED2CC1ED,
    0x20604020,
    0xFC1FE3FC,
    0xB1C879B1,
    0x5BEDB65B,
    0x6ABED46A,
    0xCB468DCB,
    0xBED967BE,
    0x394B7239,
    0x4ADE944A,
    0x4CD4984C,
    0x58E8B058,
    0xCF4A85CF,
    0xD06BBBD0,
    0xEF2AC5EF,
    0xAAE54FAA,
    0xFB16EDFB,
    0x43C58643,
    0x4DD79A4D,
    0x33556633,
    0x85941185,
    0x45CF8A45,
    0xF910E9F9,
    0x02060402,
    0x7F81FE7F,
    0x50F0A050,
    0x3C44783C,
    0x9FBA259F,
    0xA8E34BA8,
    0x51F3A251,
    0xA3FE5DA3,
    0x40C08040,
    0x8F8A058F,
    0x92AD3F92,
    0x9DBC219D,
    0x38487038,
    0xF504F1F5,
    0xBCDF63BC,
    0xB6C177B6,
    0xDA75AFDA,
    0x21634221,
    0x10302010,
    0xFF1AE5FF,
    0xF30EFDF3,
    0xD26DBFD2,
    0xCD4C81CD,
    0x0C14180C,
    0x13352613,
    0xEC2FC3EC,
    0x5FE1BE5F,
    0x97A23597,
    0x44CC8844,
    0x17392E17,
    0xC45793C4,
    0xA7F255A7,
    0x7E82FC7E,
    0x3D477A3D,
    0x64ACC864,
    0x5DE7BA5D,
    0x192B3219,
    0x7395E673,
    0x60A0C060,
    0x81981981,
    0x4FD19E4F,
    0xDC7FA3DC,
    0x22664422,
    0x2A7E542A,
    0x90AB3B90,
    0x88830B88,
    0x46CA8C46,
    0xEE29C7EE,
    0xB8D36BB8,
    0x143C2814,
    0xDE79A7DE,
    0x5EE2BC5E,
    0x0B1D160B,
    0xDB76ADDB,
    0xE03BDBE0,
    0x32566432,
    0x3A4E743A,
    0x0A1E140A,
    0x49DB9249,
    0x060A0C06,
    0x246C4824,
    0x5CE4B85C,
    0xC25D9FC2,
    0xD36EBDD3,
    0xACEF43AC,
    0x62A6C462,
    0x91A83991,
    0x95A43195,
    0xE437D3E4,
    0x798BF279,
    0xE732D5E7,
    0xC8438BC8,
    0x37596E37,
    0x6DB7DA6D,
    0x8D8C018D,
    0xD564B1D5,
    0x4ED29C4E,
    0xA9E049A9,
    0x6CB4D86C,
    0x56FAAC56,
    0xF407F3F4,
    0xEA25CFEA,
    0x65AFCA65,
    0x7A8EF47A,
    0xAEE947AE,
    0x08181008,
    0xBAD56FBA,
    0x7888F078,
    0x256F4A25,
    0x2E725C2E,
    0x1C24381C,
    0xA6F157A6,
    0xB4C773B4,
    0xC65197C6,
    0xE823CBE8,
    0xDD7CA1DD,
    0x749CE874,
    0x1F213E1F,
    0x4BDD964B,
    0xBDDC61BD,
    0x8B860D8B,
    0x8A850F8A,
    0x7090E070,
    0x3E427C3E,
    0xB5C471B5,
    0x66AACC66,
    0x48D89048,
    0x03050603,
    0xF601F7F6,
    0x0E121C0E,
    0x61A3C261,
    0x355F6A35,
    0x57F9AE57,
    0xB9D069B9,
    0x86911786,
    0xC15899C1,
    0x1D273A1D,
    0x9EB9279E,
    0xE138D9E1,
    0xF813EBF8,
    0x98B32B98,
    0x11332211,
    0x69BBD269,
    0xD970A9D9,
    0x8E89078E,
    0x94A73394,
    0x9BB62D9B,
    0x1E223C1E,
    0x87921587,
    0xE920C9E9,
    0xCE4987CE,
    0x55FFAA55,
    0x28785028,
    0xDF7AA5DF,
    0x8C8F038C,
    0xA1F859A1,
    0x89800989,
    0x0D171A0D,
    0xBFDA65BF,
    0xE631D7E6,
    0x42C68442,
    0x68B8D068,
    0x41C38241,
    0x99B02999,
    0x2D775A2D,
    0x0F111E0F,
    0xB0CB7BB0,
    0x54FCA854,
    0xBBD66DBB,
    0x163A2C16,
  ]
  T4 = [
    0x6363A5C6,
    0x7C7C84F8,
    0x777799EE,
    0x7B7B8DF6,
    0xF2F20DFF,
    0x6B6BBDD6,
    0x6F6FB1DE,
    0xC5C55491,
    0x30305060,
    0x01010302,
    0x6767A9CE,
    0x2B2B7D56,
    0xFEFE19E7,
    0xD7D762B5,
    0xABABE64D,
    0x76769AEC,
    0xCACA458F,
    0x82829D1F,
    0xC9C94089,
    0x7D7D87FA,
    0xFAFA15EF,
    0x5959EBB2,
    0x4747C98E,
    0xF0F00BFB,
    0xADADEC41,
    0xD4D467B3,
    0xA2A2FD5F,
    0xAFAFEA45,
    0x9C9CBF23,
    0xA4A4F753,
    0x727296E4,
    0xC0C05B9B,
    0xB7B7C275,
    0xFDFD1CE1,
    0x9393AE3D,
    0x26266A4C,
    0x36365A6C,
    0x3F3F417E,
    0xF7F702F5,
    0xCCCC4F83,
    0x34345C68,
    0xA5A5F451,
    0xE5E534D1,
    0xF1F108F9,
    0x717193E2,
    0xD8D873AB,
    0x31315362,
    0x15153F2A,
    0x04040C08,
    0xC7C75295,
    0x23236546,
    0xC3C35E9D,
    0x18182830,
    0x9696A137,
    0x05050F0A,
    0x9A9AB52F,
    0x0707090E,
    0x12123624,
    0x80809B1B,
    0xE2E23DDF,
    0xEBEB26CD,
    0x2727694E,
    0xB2B2CD7F,
    0x75759FEA,
    0x09091B12,
    0x83839E1D,
    0x2C2C7458,
    0x1A1A2E34,
    0x1B1B2D36,
    0x6E6EB2DC,
    0x5A5AEEB4,
    0xA0A0FB5B,
    0x5252F6A4,
    0x3B3B4D76,
    0xD6D661B7,
    0xB3B3CE7D,
    0x29297B52,
    0xE3E33EDD,
    0x2F2F715E,
    0x84849713,
    0x5353F5A6,
    0xD1D168B9,
    0x00000000,
    0xEDED2CC1,
    0x20206040,
    0xFCFC1FE3,
    0xB1B1C879,
    0x5B5BEDB6,
    0x6A6ABED4,
    0xCBCB468D,
    0xBEBED967,
    0x39394B72,
    0x4A4ADE94,
    0x4C4CD498,
    0x5858E8B0,
    0xCFCF4A85,
    0xD0D06BBB,
    0xEFEF2AC5,
    0xAAAAE54F,
    0xFBFB16ED,
    0x4343C586,
    0x4D4DD79A,
    0x33335566,
    0x85859411,
    0x4545CF8A,
    0xF9F910E9,
    0x02020604,
    0x7F7F81FE,
    0x5050F0A0,
    0x3C3C4478,
    0x9F9FBA25,
    0xA8A8E34B,
    0x5151F3A2,
    0xA3A3FE5D,
    0x4040C080,
    0x8F8F8A05,
    0x9292AD3F,
    0x9D9DBC21,
    0x38384870,
    0xF5F504F1,
    0xBCBCDF63,
    0xB6B6C177,
    0xDADA75AF,
    0x21216342,
    0x10103020,
    0xFFFF1AE5,
    0xF3F30EFD,
    0xD2D26DBF,
    0xCDCD4C81,
    0x0C0C1418,
    0x13133526,
    0xECEC2FC3,
    0x5F5FE1BE,
    0x9797A235,
    0x4444CC88,
    0x1717392E,
    0xC4C45793,
    0xA7A7F255,
    0x7E7E82FC,
    0x3D3D477A,
    0x6464ACC8,
    0x5D5DE7BA,
    0x19192B32,
    0x737395E6,
    0x6060A0C0,
    0x81819819,
    0x4F4FD19E,
    0xDCDC7FA3,
    0x22226644,
    0x2A2A7E54,
    0x9090AB3B,
    0x8888830B,
    0x4646CA8C,
    0xEEEE29C7,
    0xB8B8D36B,
    0x14143C28,
    0xDEDE79A7,
    0x5E5EE2BC,
    0x0B0B1D16,
    0xDBDB76AD,
    0xE0E03BDB,
    0x32325664,
    0x3A3A4E74,
    0x0A0A1E14,
    0x4949DB92,
    0x06060A0C,
    0x24246C48,
    0x5C5CE4B8,
    0xC2C25D9F,
    0xD3D36EBD,
    0xACACEF43,
    0x6262A6C4,
    0x9191A839,
    0x9595A431,
    0xE4E437D3,
    0x79798BF2,
    0xE7E732D5,
    0xC8C8438B,
    0x3737596E,
    0x6D6DB7DA,
    0x8D8D8C01,
    0xD5D564B1,
    0x4E4ED29C,
    0xA9A9E049,
    0x6C6CB4D8,
    0x5656FAAC,
    0xF4F407F3,
    0xEAEA25CF,
    0x6565AFCA,
    0x7A7A8EF4,
    0xAEAEE947,
    0x08081810,
    0xBABAD56F,
    0x787888F0,
    0x25256F4A,
    0x2E2E725C,
    0x1C1C2438,
    0xA6A6F157,
    0xB4B4C773,
    0xC6C65197,
    0xE8E823CB,
    0xDDDD7CA1,
    0x74749CE8,
    0x1F1F213E,
    0x4B4BDD96,
    0xBDBDDC61,
    0x8B8B860D,
    0x8A8A850F,
    0x707090E0,
    0x3E3E427C,
    0xB5B5C471,
    0x6666AACC,
    0x4848D890,
    0x03030506,
    0xF6F601F7,
    0x0E0E121C,
    0x6161A3C2,
    0x35355F6A,
    0x5757F9AE,
    0xB9B9D069,
    0x86869117,
    0xC1C15899,
    0x1D1D273A,
    0x9E9EB927,
    0xE1E138D9,
    0xF8F813EB,
    0x9898B32B,
    0x11113322,
    0x6969BBD2,
    0xD9D970A9,
    0x8E8E8907,
    0x9494A733,
    0x9B9BB62D,
    0x1E1E223C,
    0x87879215,
    0xE9E920C9,
    0xCECE4987,
    0x5555FFAA,
    0x28287850,
    0xDFDF7AA5,
    0x8C8C8F03,
    0xA1A1F859,
    0x89898009,
    0x0D0D171A,
    0xBFBFDA65,
    0xE6E631D7,
    0x4242C684,
    0x6868B8D0,
    0x4141C382,
    0x9999B029,
    0x2D2D775A,
    0x0F0F111E,
    0xB0B0CB7B,
    0x5454FCA8,
    0xBBBBD66D,
    0x16163A2C,
  ]

  # Transformations for decryption
  T5 = [
    0x51F4A750,
    0x7E416553,
    0x1A17A4C3,
    0x3A275E96,
    0x3BAB6BCB,
    0x1F9D45F1,
    0xACFA58AB,
    0x4BE30393,
    0x2030FA55,
    0xAD766DF6,
    0x88CC7691,
    0xF5024C25,
    0x4FE5D7FC,
    0xC52ACBD7,
    0x26354480,
    0xB562A38F,
    0xDEB15A49,
    0x25BA1B67,
    0x45EA0E98,
    0x5DFEC0E1,
    0xC32F7502,
    0x814CF012,
    0x8D4697A3,
    0x6BD3F9C6,
    0x038F5FE7,
    0x15929C95,
    0xBF6D7AEB,
    0x955259DA,
    0xD4BE832D,
    0x587421D3,
    0x49E06929,
    0x8EC9C844,
    0x75C2896A,
    0xF48E7978,
    0x99583E6B,
    0x27B971DD,
    0xBEE14FB6,
    0xF088AD17,
    0xC920AC66,
    0x7DCE3AB4,
    0x63DF4A18,
    0xE51A3182,
    0x97513360,
    0x62537F45,
    0xB16477E0,
    0xBB6BAE84,
    0xFE81A01C,
    0xF9082B94,
    0x70486858,
    0x8F45FD19,
    0x94DE6C87,
    0x527BF8B7,
    0xAB73D323,
    0x724B02E2,
    0xE31F8F57,
    0x6655AB2A,
    0xB2EB2807,
    0x2FB5C203,
    0x86C57B9A,
    0xD33708A5,
    0x302887F2,
    0x23BFA5B2,
    0x02036ABA,
    0xED16825C,
    0x8ACF1C2B,
    0xA779B492,
    0xF307F2F0,
    0x4E69E2A1,
    0x65DAF4CD,
    0x0605BED5,
    0xD134621F,
    0xC4A6FE8A,
    0x342E539D,
    0xA2F355A0,
    0x058AE132,
    0xA4F6EB75,
    0x0B83EC39,
    0x4060EFAA,
    0x5E719F06,
    0xBD6E1051,
    0x3E218AF9,
    0x96DD063D,
    0xDD3E05AE,
    0x4DE6BD46,
    0x91548DB5,
    0x71C45D05,
    0x0406D46F,
    0x605015FF,
    0x1998FB24,
    0xD6BDE997,
    0x894043CC,
    0x67D99E77,
    0xB0E842BD,
    0x07898B88,
    0xE7195B38,
    0x79C8EEDB,
    0xA17C0A47,
    0x7C420FE9,
    0xF8841EC9,
    0x00000000,
    0x09808683,
    0x322BED48,
    0x1E1170AC,
    0x6C5A724E,
    0xFD0EFFFB,
    0x0F853856,
    0x3DAED51E,
    0x362D3927,
    0x0A0FD964,
    0x685CA621,
    0x9B5B54D1,
    0x24362E3A,
    0x0C0A67B1,
    0x9357E70F,
    0xB4EE96D2,
    0x1B9B919E,
    0x80C0C54F,
    0x61DC20A2,
    0x5A774B69,
    0x1C121A16,
    0xE293BA0A,
    0xC0A02AE5,
    0x3C22E043,
    0x121B171D,
    0x0E090D0B,
    0xF28BC7AD,
    0x2DB6A8B9,
    0x141EA9C8,
    0x57F11985,
    0xAF75074C,
    0xEE99DDBB,
    0xA37F60FD,
    0xF701269F,
    0x5C72F5BC,
    0x44663BC5,
    0x5BFB7E34,
    0x8B432976,
    0xCB23C6DC,
    0xB6EDFC68,
    0xB8E4F163,
    0xD731DCCA,
    0x42638510,
    0x13972240,
    0x84C61120,
    0x854A247D,
    0xD2BB3DF8,
    0xAEF93211,
    0xC729A16D,
    0x1D9E2F4B,
    0xDCB230F3,
    0x0D8652EC,
    0x77C1E3D0,
    0x2BB3166C,
    0xA970B999,
    0x119448FA,
    0x47E96422,
    0xA8FC8CC4,
    0xA0F03F1A,
    0x567D2CD8,
    0x223390EF,
    0x87494EC7,
    0xD938D1C1,
    0x8CCAA2FE,
    0x98D40B36,
    0xA6F581CF,
    0xA57ADE28,
    0xDAB78E26,
    0x3FADBFA4,
    0x2C3A9DE4,
    0x5078920D,
    0x6A5FCC9B,
    0x547E4662,
    0xF68D13C2,
    0x90D8B8E8,
    0x2E39F75E,
    0x82C3AFF5,
    0x9F5D80BE,
    0x69D0937C,
    0x6FD52DA9,
    0xCF2512B3,
    0xC8AC993B,
    0x10187DA7,
    0xE89C636E,
    0xDB3BBB7B,
    0xCD267809,
    0x6E5918F4,
    0xEC9AB701,
    0x834F9AA8,
    0xE6956E65,
    0xAAFFE67E,
    0x21BCCF08,
    0xEF15E8E6,
    0xBAE79BD9,
    0x4A6F36CE,
    0xEA9F09D4,
    0x29B07CD6,
    0x31A4B2AF,
    0x2A3F2331,
    0xC6A59430,
    0x35A266C0,
    0x744EBC37,
    0xFC82CAA6,
    0xE090D0B0,
    0x33A7D815,
    0xF104984A,
    0x41ECDAF7,
    0x7FCD500E,
    0x1791F62F,
    0x764DD68D,
    0x43EFB04D,
    0xCCAA4D54,
    0xE49604DF,
    0x9ED1B5E3,
    0x4C6A881B,
    0xC12C1FB8,
    0x4665517F,
    0x9D5EEA04,
    0x018C355D,
    0xFA877473,
    0xFB0B412E,
    0xB3671D5A,
    0x92DBD252,
    0xE9105633,
    0x6DD64713,
    0x9AD7618C,
    0x37A10C7A,
    0x59F8148E,
    0xEB133C89,
    0xCEA927EE,
    0xB761C935,
    0xE11CE5ED,
    0x7A47B13C,
    0x9CD2DF59,
    0x55F2733F,
    0x1814CE79,
    0x73C737BF,
    0x53F7CDEA,
    0x5FFDAA5B,
    0xDF3D6F14,
    0x7844DB86,
    0xCAAFF381,
    0xB968C43E,
    0x3824342C,
    0xC2A3405F,
    0x161DC372,
    0xBCE2250C,
    0x283C498B,
    0xFF0D9541,
    0x39A80171,
    0x080CB3DE,
    0xD8B4E49C,
    0x6456C190,
    0x7BCB8461,
    0xD532B670,
    0x486C5C74,
    0xD0B85742,
  ]
  T6 = [
    0x5051F4A7,
    0x537E4165,
    0xC31A17A4,
    0x963A275E,
    0xCB3BAB6B,
    0xF11F9D45,
    0xABACFA58,
    0x934BE303,
    0x552030FA,
    0xF6AD766D,
    0x9188CC76,
    0x25F5024C,
    0xFC4FE5D7,
    0xD7C52ACB,
    0x80263544,
    0x8FB562A3,
    0x49DEB15A,
    0x6725BA1B,
    0x9845EA0E,
    0xE15DFEC0,
    0x02C32F75,
    0x12814CF0,
    0xA38D4697,
    0xC66BD3F9,
    0xE7038F5F,
    0x9515929C,
    0xEBBF6D7A,
    0xDA955259,
    0x2DD4BE83,
    0xD3587421,
    0x2949E069,
    0x448EC9C8,
    0x6A75C289,
    0x78F48E79,
    0x6B99583E,
    0xDD27B971,
    0xB6BEE14F,
    0x17F088AD,
    0x66C920AC,
    0xB47DCE3A,
    0x1863DF4A,
    0x82E51A31,
    0x60975133,
    0x4562537F,
    0xE0B16477,
    0x84BB6BAE,
    0x1CFE81A0,
    0x94F9082B,
    0x58704868,
    0x198F45FD,
    0x8794DE6C,
    0xB7527BF8,
    0x23AB73D3,
    0xE2724B02,
    0x57E31F8F,
    0x2A6655AB,
    0x07B2EB28,
    0x032FB5C2,
    0x9A86C57B,
    0xA5D33708,
    0xF2302887,
    0xB223BFA5,
    0xBA02036A,
    0x5CED1682,
    0x2B8ACF1C,
    0x92A779B4,
    0xF0F307F2,
    0xA14E69E2,
    0xCD65DAF4,
    0xD50605BE,
    0x1FD13462,
    0x8AC4A6FE,
    0x9D342E53,
    0xA0A2F355,
    0x32058AE1,
    0x75A4F6EB,
    0x390B83EC,
    0xAA4060EF,
    0x065E719F,
    0x51BD6E10,
    0xF93E218A,
    0x3D96DD06,
    0xAEDD3E05,
    0x464DE6BD,
    0xB591548D,
    0x0571C45D,
    0x6F0406D4,
    0xFF605015,
    0x241998FB,
    0x97D6BDE9,
    0xCC894043,
    0x7767D99E,
    0xBDB0E842,
    0x8807898B,
    0x38E7195B,
    0xDB79C8EE,
    0x47A17C0A,
    0xE97C420F,
    0xC9F8841E,
    0x00000000,
    0x83098086,
    0x48322BED,
    0xAC1E1170,
    0x4E6C5A72,
    0xFBFD0EFF,
    0x560F8538,
    0x1E3DAED5,
    0x27362D39,
    0x640A0FD9,
    0x21685CA6,
    0xD19B5B54,
    0x3A24362E,
    0xB10C0A67,
    0x0F9357E7,
    0xD2B4EE96,
    0x9E1B9B91,
    0x4F80C0C5,
    0xA261DC20,
    0x695A774B,
    0x161C121A,
    0x0AE293BA,
    0xE5C0A02A,
    0x433C22E0,
    0x1D121B17,
    0x0B0E090D,
    0xADF28BC7,
    0xB92DB6A8,
    0xC8141EA9,
    0x8557F119,
    0x4CAF7507,
    0xBBEE99DD,
    0xFDA37F60,
    0x9FF70126,
    0xBC5C72F5,
    0xC544663B,
    0x345BFB7E,
    0x768B4329,
    0xDCCB23C6,
    0x68B6EDFC,
    0x63B8E4F1,
    0xCAD731DC,
    0x10426385,
    0x40139722,
    0x2084C611,
    0x7D854A24,
    0xF8D2BB3D,
    0x11AEF932,
    0x6DC729A1,
    0x4B1D9E2F,
    0xF3DCB230,
    0xEC0D8652,
    0xD077C1E3,
    0x6C2BB316,
    0x99A970B9,
    0xFA119448,
    0x2247E964,
    0xC4A8FC8C,
    0x1AA0F03F,
    0xD8567D2C,
    0xEF223390,
    0xC787494E,
    0xC1D938D1,
    0xFE8CCAA2,
    0x3698D40B,
    0xCFA6F581,
    0x28A57ADE,
    0x26DAB78E,
    0xA43FADBF,
    0xE42C3A9D,
    0x0D507892,
    0x9B6A5FCC,
    0x62547E46,
    0xC2F68D13,
    0xE890D8B8,
    0x5E2E39F7,
    0xF582C3AF,
    0xBE9F5D80,
    0x7C69D093,
    0xA96FD52D,
    0xB3CF2512,
    0x3BC8AC99,
    0xA710187D,
    0x6EE89C63,
    0x7BDB3BBB,
    0x09CD2678,
    0xF46E5918,
    0x01EC9AB7,
    0xA8834F9A,
    0x65E6956E,
    0x7EAAFFE6,
    0x0821BCCF,
    0xE6EF15E8,
    0xD9BAE79B,
    0xCE4A6F36,
    0xD4EA9F09,
    0xD629B07C,
    0xAF31A4B2,
    0x312A3F23,
    0x30C6A594,
    0xC035A266,
    0x37744EBC,
    0xA6FC82CA,
    0xB0E090D0,
    0x1533A7D8,
    0x4AF10498,
    0xF741ECDA,
    0x0E7FCD50,
    0x2F1791F6,
    0x8D764DD6,
    0x4D43EFB0,
    0x54CCAA4D,
    0xDFE49604,
    0xE39ED1B5,
    0x1B4C6A88,
    0xB8C12C1F,
    0x7F466551,
    0x049D5EEA,
    0x5D018C35,
    0x73FA8774,
    0x2EFB0B41,
    0x5AB3671D,
    0x5292DBD2,
    0x33E91056,
    0x136DD647,
    0x8C9AD761,
    0x7A37A10C,
    0x8E59F814,
    0x89EB133C,
    0xEECEA927,
    0x35B761C9,
    0xEDE11CE5,
    0x3C7A47B1,
    0x599CD2DF,
    0x3F55F273,
    0x791814CE,
    0xBF73C737,
    0xEA53F7CD,
    0x5B5FFDAA,
    0x14DF3D6F,
    0x867844DB,
    0x81CAAFF3,
    0x3EB968C4,
    0x2C382434,
    0x5FC2A340,
    0x72161DC3,
    0x0CBCE225,
    0x8B283C49,
    0x41FF0D95,
    0x7139A801,
    0xDE080CB3,
    0x9CD8B4E4,
    0x906456C1,
    0x617BCB84,
    0x70D532B6,
    0x74486C5C,
    0x42D0B857,
  ]
  T7 = [
    0xA75051F4,
    0x65537E41,
    0xA4C31A17,
    0x5E963A27,
    0x6BCB3BAB,
    0x45F11F9D,
    0x58ABACFA,
    0x03934BE3,
    0xFA552030,
    0x6DF6AD76,
    0x769188CC,
    0x4C25F502,
    0xD7FC4FE5,
    0xCBD7C52A,
    0x44802635,
    0xA38FB562,
    0x5A49DEB1,
    0x1B6725BA,
    0x0E9845EA,
    0xC0E15DFE,
    0x7502C32F,
    0xF012814C,
    0x97A38D46,
    0xF9C66BD3,
    0x5FE7038F,
    0x9C951592,
    0x7AEBBF6D,
    0x59DA9552,
    0x832DD4BE,
    0x21D35874,
    0x692949E0,
    0xC8448EC9,
    0x896A75C2,
    0x7978F48E,
    0x3E6B9958,
    0x71DD27B9,
    0x4FB6BEE1,
    0xAD17F088,
    0xAC66C920,
    0x3AB47DCE,
    0x4A1863DF,
    0x3182E51A,
    0x33609751,
    0x7F456253,
    0x77E0B164,
    0xAE84BB6B,
    0xA01CFE81,
    0x2B94F908,
    0x68587048,
    0xFD198F45,
    0x6C8794DE,
    0xF8B7527B,
    0xD323AB73,
    0x02E2724B,
    0x8F57E31F,
    0xAB2A6655,
    0x2807B2EB,
    0xC2032FB5,
    0x7B9A86C5,
    0x08A5D337,
    0x87F23028,
    0xA5B223BF,
    0x6ABA0203,
    0x825CED16,
    0x1C2B8ACF,
    0xB492A779,
    0xF2F0F307,
    0xE2A14E69,
    0xF4CD65DA,
    0xBED50605,
    0x621FD134,
    0xFE8AC4A6,
    0x539D342E,
    0x55A0A2F3,
    0xE132058A,
    0xEB75A4F6,
    0xEC390B83,
    0xEFAA4060,
    0x9F065E71,
    0x1051BD6E,
    0x8AF93E21,
    0x063D96DD,
    0x05AEDD3E,
    0xBD464DE6,
    0x8DB59154,
    0x5D0571C4,
    0xD46F0406,
    0x15FF6050,
    0xFB241998,
    0xE997D6BD,
    0x43CC8940,
    0x9E7767D9,
    0x42BDB0E8,
    0x8B880789,
    0x5B38E719,
    0xEEDB79C8,
    0x0A47A17C,
    0x0FE97C42,
    0x1EC9F884,
    0x00000000,
    0x86830980,
    0xED48322B,
    0x70AC1E11,
    0x724E6C5A,
    0xFFFBFD0E,
    0x38560F85,
    0xD51E3DAE,
    0x3927362D,
    0xD9640A0F,
    0xA621685C,
    0x54D19B5B,
    0x2E3A2436,
    0x67B10C0A,
    0xE70F9357,
    0x96D2B4EE,
    0x919E1B9B,
    0xC54F80C0,
    0x20A261DC,
    0x4B695A77,
    0x1A161C12,
    0xBA0AE293,
    0x2AE5C0A0,
    0xE0433C22,
    0x171D121B,
    0x0D0B0E09,
    0xC7ADF28B,
    0xA8B92DB6,
    0xA9C8141E,
    0x198557F1,
    0x074CAF75,
    0xDDBBEE99,
    0x60FDA37F,
    0x269FF701,
    0xF5BC5C72,
    0x3BC54466,
    0x7E345BFB,
    0x29768B43,
    0xC6DCCB23,
    0xFC68B6ED,
    0xF163B8E4,
    0xDCCAD731,
    0x85104263,
    0x22401397,
    0x112084C6,
    0x247D854A,
    0x3DF8D2BB,
    0x3211AEF9,
    0xA16DC729,
    0x2F4B1D9E,
    0x30F3DCB2,
    0x52EC0D86,
    0xE3D077C1,
    0x166C2BB3,
    0xB999A970,
    0x48FA1194,
    0x642247E9,
    0x8CC4A8FC,
    0x3F1AA0F0,
    0x2CD8567D,
    0x90EF2233,
    0x4EC78749,
    0xD1C1D938,
    0xA2FE8CCA,
    0x0B3698D4,
    0x81CFA6F5,
    0xDE28A57A,
    0x8E26DAB7,
    0xBFA43FAD,
    0x9DE42C3A,
    0x920D5078,
    0xCC9B6A5F,
    0x4662547E,
    0x13C2F68D,
    0xB8E890D8,
    0xF75E2E39,
    0xAFF582C3,
    0x80BE9F5D,
    0x937C69D0,
    0x2DA96FD5,
    0x12B3CF25,
    0x993BC8AC,
    0x7DA71018,
    0x636EE89C,
    0xBB7BDB3B,
    0x7809CD26,
    0x18F46E59,
    0xB701EC9A,
    0x9AA8834F,
    0x6E65E695,
    0xE67EAAFF,
    0xCF0821BC,
    0xE8E6EF15,
    0x9BD9BAE7,
    0x36CE4A6F,
    0x09D4EA9F,
    0x7CD629B0,
    0xB2AF31A4,
    0x23312A3F,
    0x9430C6A5,
    0x66C035A2,
    0xBC37744E,
    0xCAA6FC82,
    0xD0B0E090,
    0xD81533A7,
    0x984AF104,
    0xDAF741EC,
    0x500E7FCD,
    0xF62F1791,
    0xD68D764D,
    0xB04D43EF,
    0x4D54CCAA,
    0x04DFE496,
    0xB5E39ED1,
    0x881B4C6A,
    0x1FB8C12C,
    0x517F4665,
    0xEA049D5E,
    0x355D018C,
    0x7473FA87,
    0x412EFB0B,
    0x1D5AB367,
    0xD25292DB,
    0x5633E910,
    0x47136DD6,
    0x618C9AD7,
    0x0C7A37A1,
    0x148E59F8,
    0x3C89EB13,
    0x27EECEA9,
    0xC935B761,
    0xE5EDE11C,
    0xB13C7A47,
    0xDF599CD2,
    0x733F55F2,
    0xCE791814,
    0x37BF73C7,
    0xCDEA53F7,
    0xAA5B5FFD,
    0x6F14DF3D,
    0xDB867844,
    0xF381CAAF,
    0xC43EB968,
    0x342C3824,
    0x405FC2A3,
    0xC372161D,
    0x250CBCE2,
    0x498B283C,
    0x9541FF0D,
    0x017139A8,
    0xB3DE080C,
    0xE49CD8B4,
    0xC1906456,
    0x84617BCB,
    0xB670D532,
    0x5C74486C,
    0x5742D0B8,
  ]
  T8 = [
    0xF4A75051,
    0x4165537E,
    0x17A4C31A,
    0x275E963A,
    0xAB6BCB3B,
    0x9D45F11F,
    0xFA58ABAC,
    0xE303934B,
    0x30FA5520,
    0x766DF6AD,
    0xCC769188,
    0x024C25F5,
    0xE5D7FC4F,
    0x2ACBD7C5,
    0x35448026,
    0x62A38FB5,
    0xB15A49DE,
    0xBA1B6725,
    0xEA0E9845,
    0xFEC0E15D,
    0x2F7502C3,
    0x4CF01281,
    0x4697A38D,
    0xD3F9C66B,
    0x8F5FE703,
    0x929C9515,
    0x6D7AEBBF,
    0x5259DA95,
    0xBE832DD4,
    0x7421D358,
    0xE0692949,
    0xC9C8448E,
    0xC2896A75,
    0x8E7978F4,
    0x583E6B99,
    0xB971DD27,
    0xE14FB6BE,
    0x88AD17F0,
    0x20AC66C9,
    0xCE3AB47D,
    0xDF4A1863,
    0x1A3182E5,
    0x51336097,
    0x537F4562,
    0x6477E0B1,
    0x6BAE84BB,
    0x81A01CFE,
    0x082B94F9,
    0x48685870,
    0x45FD198F,
    0xDE6C8794,
    0x7BF8B752,
    0x73D323AB,
    0x4B02E272,
    0x1F8F57E3,
    0x55AB2A66,
    0xEB2807B2,
    0xB5C2032F,
    0xC57B9A86,
    0x3708A5D3,
    0x2887F230,
    0xBFA5B223,
    0x036ABA02,
    0x16825CED,
    0xCF1C2B8A,
    0x79B492A7,
    0x07F2F0F3,
    0x69E2A14E,
    0xDAF4CD65,
    0x05BED506,
    0x34621FD1,
    0xA6FE8AC4,
    0x2E539D34,
    0xF355A0A2,
    0x8AE13205,
    0xF6EB75A4,
    0x83EC390B,
    0x60EFAA40,
    0x719F065E,
    0x6E1051BD,
    0x218AF93E,
    0xDD063D96,
    0x3E05AEDD,
    0xE6BD464D,
    0x548DB591,
    0xC45D0571,
    0x06D46F04,
    0x5015FF60,
    0x98FB2419,
    0xBDE997D6,
    0x4043CC89,
    0xD99E7767,
    0xE842BDB0,
    0x898B8807,
    0x195B38E7,
    0xC8EEDB79,
    0x7C0A47A1,
    0x420FE97C,
    0x841EC9F8,
    0x00000000,
    0x80868309,
    0x2BED4832,
    0x1170AC1E,
    0x5A724E6C,
    0x0EFFFBFD,
    0x8538560F,
    0xAED51E3D,
    0x2D392736,
    0x0FD9640A,
    0x5CA62168,
    0x5B54D19B,
    0x362E3A24,
    0x0A67B10C,
    0x57E70F93,
    0xEE96D2B4,
    0x9B919E1B,
    0xC0C54F80,
    0xDC20A261,
    0x774B695A,
    0x121A161C,
    0x93BA0AE2,
    0xA02AE5C0,
    0x22E0433C,
    0x1B171D12,
    0x090D0B0E,
    0x8BC7ADF2,
    0xB6A8B92D,
    0x1EA9C814,
    0xF1198557,
    0x75074CAF,
    0x99DDBBEE,
    0x7F60FDA3,
    0x01269FF7,
    0x72F5BC5C,
    0x663BC544,
    0xFB7E345B,
    0x4329768B,
    0x23C6DCCB,
    0xEDFC68B6,
    0xE4F163B8,
    0x31DCCAD7,
    0x63851042,
    0x97224013,
    0xC6112084,
    0x4A247D85,
    0xBB3DF8D2,
    0xF93211AE,
    0x29A16DC7,
    0x9E2F4B1D,
    0xB230F3DC,
    0x8652EC0D,
    0xC1E3D077,
    0xB3166C2B,
    0x70B999A9,
    0x9448FA11,
    0xE9642247,
    0xFC8CC4A8,
    0xF03F1AA0,
    0x7D2CD856,
    0x3390EF22,
    0x494EC787,
    0x38D1C1D9,
    0xCAA2FE8C,
    0xD40B3698,
    0xF581CFA6,
    0x7ADE28A5,
    0xB78E26DA,
    0xADBFA43F,
    0x3A9DE42C,
    0x78920D50,
    0x5FCC9B6A,
    0x7E466254,
    0x8D13C2F6,
    0xD8B8E890,
    0x39F75E2E,
    0xC3AFF582,
    0x5D80BE9F,
    0xD0937C69,
    0xD52DA96F,
    0x2512B3CF,
    0xAC993BC8,
    0x187DA710,
    0x9C636EE8,
    0x3BBB7BDB,
    0x267809CD,
    0x5918F46E,
    0x9AB701EC,
    0x4F9AA883,
    0x956E65E6,
    0xFFE67EAA,
    0xBCCF0821,
    0x15E8E6EF,
    0xE79BD9BA,
    0x6F36CE4A,
    0x9F09D4EA,
    0xB07CD629,
    0xA4B2AF31,
    0x3F23312A,
    0xA59430C6,
    0xA266C035,
    0x4EBC3774,
    0x82CAA6FC,
    0x90D0B0E0,
    0xA7D81533,
    0x04984AF1,
    0xECDAF741,
    0xCD500E7F,
    0x91F62F17,
    0x4DD68D76,
    0xEFB04D43,
    0xAA4D54CC,
    0x9604DFE4,
    0xD1B5E39E,
    0x6A881B4C,
    0x2C1FB8C1,
    0x65517F46,
    0x5EEA049D,
    0x8C355D01,
    0x877473FA,
    0x0B412EFB,
    0x671D5AB3,
    0xDBD25292,
    0x105633E9,
    0xD647136D,
    0xD7618C9A,
    0xA10C7A37,
    0xF8148E59,
    0x133C89EB,
    0xA927EECE,
    0x61C935B7,
    0x1CE5EDE1,
    0x47B13C7A,
    0xD2DF599C,
    0xF2733F55,
    0x14CE7918,
    0xC737BF73,
    0xF7CDEA53,
    0xFDAA5B5F,
    0x3D6F14DF,
    0x44DB8678,
    0xAFF381CA,
    0x68C43EB9,
    0x24342C38,
    0xA3405FC2,
    0x1DC37216,
    0xE2250CBC,
    0x3C498B28,
    0x0D9541FF,
    0xA8017139,
    0x0CB3DE08,
    0xB4E49CD8,
    0x56C19064,
    0xCB84617B,
    0x32B670D5,
    0x6C5C7448,
    0xB85742D0,
  ]

  # Transformations for decryption key expansion
  U1 = [
    0x00000000,
    0x0E090D0B,
    0x1C121A16,
    0x121B171D,
    0x3824342C,
    0x362D3927,
    0x24362E3A,
    0x2A3F2331,
    0x70486858,
    0x7E416553,
    0x6C5A724E,
    0x62537F45,
    0x486C5C74,
    0x4665517F,
    0x547E4662,
    0x5A774B69,
    0xE090D0B0,
    0xEE99DDBB,
    0xFC82CAA6,
    0xF28BC7AD,
    0xD8B4E49C,
    0xD6BDE997,
    0xC4A6FE8A,
    0xCAAFF381,
    0x90D8B8E8,
    0x9ED1B5E3,
    0x8CCAA2FE,
    0x82C3AFF5,
    0xA8FC8CC4,
    0xA6F581CF,
    0xB4EE96D2,
    0xBAE79BD9,
    0xDB3BBB7B,
    0xD532B670,
    0xC729A16D,
    0xC920AC66,
    0xE31F8F57,
    0xED16825C,
    0xFF0D9541,
    0xF104984A,
    0xAB73D323,
    0xA57ADE28,
    0xB761C935,
    0xB968C43E,
    0x9357E70F,
    0x9D5EEA04,
    0x8F45FD19,
    0x814CF012,
    0x3BAB6BCB,
    0x35A266C0,
    0x27B971DD,
    0x29B07CD6,
    0x038F5FE7,
    0x0D8652EC,
    0x1F9D45F1,
    0x119448FA,
    0x4BE30393,
    0x45EA0E98,
    0x57F11985,
    0x59F8148E,
    0x73C737BF,
    0x7DCE3AB4,
    0x6FD52DA9,
    0x61DC20A2,
    0xAD766DF6,
    0xA37F60FD,
    0xB16477E0,
    0xBF6D7AEB,
    0x955259DA,
    0x9B5B54D1,
    0x894043CC,
    0x87494EC7,
    0xDD3E05AE,
    0xD33708A5,
    0xC12C1FB8,
    0xCF2512B3,
    0xE51A3182,
    0xEB133C89,
    0xF9082B94,
    0xF701269F,
    0x4DE6BD46,
    0x43EFB04D,
    0x51F4A750,
    0x5FFDAA5B,
    0x75C2896A,
    0x7BCB8461,
    0x69D0937C,
    0x67D99E77,
    0x3DAED51E,
    0x33A7D815,
    0x21BCCF08,
    0x2FB5C203,
    0x058AE132,
    0x0B83EC39,
    0x1998FB24,
    0x1791F62F,
    0x764DD68D,
    0x7844DB86,
    0x6A5FCC9B,
    0x6456C190,
    0x4E69E2A1,
    0x4060EFAA,
    0x527BF8B7,
    0x5C72F5BC,
    0x0605BED5,
    0x080CB3DE,
    0x1A17A4C3,
    0x141EA9C8,
    0x3E218AF9,
    0x302887F2,
    0x223390EF,
    0x2C3A9DE4,
    0x96DD063D,
    0x98D40B36,
    0x8ACF1C2B,
    0x84C61120,
    0xAEF93211,
    0xA0F03F1A,
    0xB2EB2807,
    0xBCE2250C,
    0xE6956E65,
    0xE89C636E,
    0xFA877473,
    0xF48E7978,
    0xDEB15A49,
    0xD0B85742,
    0xC2A3405F,
    0xCCAA4D54,
    0x41ECDAF7,
    0x4FE5D7FC,
    0x5DFEC0E1,
    0x53F7CDEA,
    0x79C8EEDB,
    0x77C1E3D0,
    0x65DAF4CD,
    0x6BD3F9C6,
    0x31A4B2AF,
    0x3FADBFA4,
    0x2DB6A8B9,
    0x23BFA5B2,
    0x09808683,
    0x07898B88,
    0x15929C95,
    0x1B9B919E,
    0xA17C0A47,
    0xAF75074C,
    0xBD6E1051,
    0xB3671D5A,
    0x99583E6B,
    0x97513360,
    0x854A247D,
    0x8B432976,
    0xD134621F,
    0xDF3D6F14,
    0xCD267809,
    0xC32F7502,
    0xE9105633,
    0xE7195B38,
    0xF5024C25,
    0xFB0B412E,
    0x9AD7618C,
    0x94DE6C87,
    0x86C57B9A,
    0x88CC7691,
    0xA2F355A0,
    0xACFA58AB,
    0xBEE14FB6,
    0xB0E842BD,
    0xEA9F09D4,
    0xE49604DF,
    0xF68D13C2,
    0xF8841EC9,
    0xD2BB3DF8,
    0xDCB230F3,
    0xCEA927EE,
    0xC0A02AE5,
    0x7A47B13C,
    0x744EBC37,
    0x6655AB2A,
    0x685CA621,
    0x42638510,
    0x4C6A881B,
    0x5E719F06,
    0x5078920D,
    0x0A0FD964,
    0x0406D46F,
    0x161DC372,
    0x1814CE79,
    0x322BED48,
    0x3C22E043,
    0x2E39F75E,
    0x2030FA55,
    0xEC9AB701,
    0xE293BA0A,
    0xF088AD17,
    0xFE81A01C,
    0xD4BE832D,
    0xDAB78E26,
    0xC8AC993B,
    0xC6A59430,
    0x9CD2DF59,
    0x92DBD252,
    0x80C0C54F,
    0x8EC9C844,
    0xA4F6EB75,
    0xAAFFE67E,
    0xB8E4F163,
    0xB6EDFC68,
    0x0C0A67B1,
    0x02036ABA,
    0x10187DA7,
    0x1E1170AC,
    0x342E539D,
    0x3A275E96,
    0x283C498B,
    0x26354480,
    0x7C420FE9,
    0x724B02E2,
    0x605015FF,
    0x6E5918F4,
    0x44663BC5,
    0x4A6F36CE,
    0x587421D3,
    0x567D2CD8,
    0x37A10C7A,
    0x39A80171,
    0x2BB3166C,
    0x25BA1B67,
    0x0F853856,
    0x018C355D,
    0x13972240,
    0x1D9E2F4B,
    0x47E96422,
    0x49E06929,
    0x5BFB7E34,
    0x55F2733F,
    0x7FCD500E,
    0x71C45D05,
    0x63DF4A18,
    0x6DD64713,
    0xD731DCCA,
    0xD938D1C1,
    0xCB23C6DC,
    0xC52ACBD7,
    0xEF15E8E6,
    0xE11CE5ED,
    0xF307F2F0,
    0xFD0EFFFB,
    0xA779B492,
    0xA970B999,
    0xBB6BAE84,
    0xB562A38F,
    0x9F5D80BE,
    0x91548DB5,
    0x834F9AA8,
    0x8D4697A3,
  ]
  U2 = [
    0x00000000,
    0x0B0E090D,
    0x161C121A,
    0x1D121B17,
    0x2C382434,
    0x27362D39,
    0x3A24362E,
    0x312A3F23,
    0x58704868,
    0x537E4165,
    0x4E6C5A72,
    0x4562537F,
    0x74486C5C,
    0x7F466551,
    0x62547E46,
    0x695A774B,
    0xB0E090D0,
    0xBBEE99DD,
    0xA6FC82CA,
    0xADF28BC7,
    0x9CD8B4E4,
    0x97D6BDE9,
    0x8AC4A6FE,
    0x81CAAFF3,
    0xE890D8B8,
    0xE39ED1B5,
    0xFE8CCAA2,
    0xF582C3AF,
    0xC4A8FC8C,
    0xCFA6F581,
    0xD2B4EE96,
    0xD9BAE79B,
    0x7BDB3BBB,
    0x70D532B6,
    0x6DC729A1,
    0x66C920AC,
    0x57E31F8F,
    0x5CED1682,
    0x41FF0D95,
    0x4AF10498,
    0x23AB73D3,
    0x28A57ADE,
    0x35B761C9,
    0x3EB968C4,
    0x0F9357E7,
    0x049D5EEA,
    0x198F45FD,
    0x12814CF0,
    0xCB3BAB6B,
    0xC035A266,
    0xDD27B971,
    0xD629B07C,
    0xE7038F5F,
    0xEC0D8652,
    0xF11F9D45,
    0xFA119448,
    0x934BE303,
    0x9845EA0E,
    0x8557F119,
    0x8E59F814,
    0xBF73C737,
    0xB47DCE3A,
    0xA96FD52D,
    0xA261DC20,
    0xF6AD766D,
    0xFDA37F60,
    0xE0B16477,
    0xEBBF6D7A,
    0xDA955259,
    0xD19B5B54,
    0xCC894043,
    0xC787494E,
    0xAEDD3E05,
    0xA5D33708,
    0xB8C12C1F,
    0xB3CF2512,
    0x82E51A31,
    0x89EB133C,
    0x94F9082B,
    0x9FF70126,
    0x464DE6BD,
    0x4D43EFB0,
    0x5051F4A7,
    0x5B5FFDAA,
    0x6A75C289,
    0x617BCB84,
    0x7C69D093,
    0x7767D99E,
    0x1E3DAED5,
    0x1533A7D8,
    0x0821BCCF,
    0x032FB5C2,
    0x32058AE1,
    0x390B83EC,
    0x241998FB,
    0x2F1791F6,
    0x8D764DD6,
    0x867844DB,
    0x9B6A5FCC,
    0x906456C1,
    0xA14E69E2,
    0xAA4060EF,
    0xB7527BF8,
    0xBC5C72F5,
    0xD50605BE,
    0xDE080CB3,
    0xC31A17A4,
    0xC8141EA9,
    0xF93E218A,
    0xF2302887,
    0xEF223390,
    0xE42C3A9D,
    0x3D96DD06,
    0x3698D40B,
    0x2B8ACF1C,
    0x2084C611,
    0x11AEF932,
    0x1AA0F03F,
    0x07B2EB28,
    0x0CBCE225,
    0x65E6956E,
    0x6EE89C63,
    0x73FA8774,
    0x78F48E79,
    0x49DEB15A,
    0x42D0B857,
    0x5FC2A340,
    0x54CCAA4D,
    0xF741ECDA,
    0xFC4FE5D7,
    0xE15DFEC0,
    0xEA53F7CD,
    0xDB79C8EE,
    0xD077C1E3,
    0xCD65DAF4,
    0xC66BD3F9,
    0xAF31A4B2,
    0xA43FADBF,
    0xB92DB6A8,
    0xB223BFA5,
    0x83098086,
    0x8807898B,
    0x9515929C,
    0x9E1B9B91,
    0x47A17C0A,
    0x4CAF7507,
    0x51BD6E10,
    0x5AB3671D,
    0x6B99583E,
    0x60975133,
    0x7D854A24,
    0x768B4329,
    0x1FD13462,
    0x14DF3D6F,
    0x09CD2678,
    0x02C32F75,
    0x33E91056,
    0x38E7195B,
    0x25F5024C,
    0x2EFB0B41,
    0x8C9AD761,
    0x8794DE6C,
    0x9A86C57B,
    0x9188CC76,
    0xA0A2F355,
    0xABACFA58,
    0xB6BEE14F,
    0xBDB0E842,
    0xD4EA9F09,
    0xDFE49604,
    0xC2F68D13,
    0xC9F8841E,
    0xF8D2BB3D,
    0xF3DCB230,
    0xEECEA927,
    0xE5C0A02A,
    0x3C7A47B1,
    0x37744EBC,
    0x2A6655AB,
    0x21685CA6,
    0x10426385,
    0x1B4C6A88,
    0x065E719F,
    0x0D507892,
    0x640A0FD9,
    0x6F0406D4,
    0x72161DC3,
    0x791814CE,
    0x48322BED,
    0x433C22E0,
    0x5E2E39F7,
    0x552030FA,
    0x01EC9AB7,
    0x0AE293BA,
    0x17F088AD,
    0x1CFE81A0,
    0x2DD4BE83,
    0x26DAB78E,
    0x3BC8AC99,
    0x30C6A594,
    0x599CD2DF,
    0x5292DBD2,
    0x4F80C0C5,
    0x448EC9C8,
    0x75A4F6EB,
    0x7EAAFFE6,
    0x63B8E4F1,
    0x68B6EDFC,
    0xB10C0A67,
    0xBA02036A,
    0xA710187D,
    0xAC1E1170,
    0x9D342E53,
    0x963A275E,
    0x8B283C49,
    0x80263544,
    0xE97C420F,
    0xE2724B02,
    0xFF605015,
    0xF46E5918,
    0xC544663B,
    0xCE4A6F36,
    0xD3587421,
    0xD8567D2C,
    0x7A37A10C,
    0x7139A801,
    0x6C2BB316,
    0x6725BA1B,
    0x560F8538,
    0x5D018C35,
    0x40139722,
    0x4B1D9E2F,
    0x2247E964,
    0x2949E069,
    0x345BFB7E,
    0x3F55F273,
    0x0E7FCD50,
    0x0571C45D,
    0x1863DF4A,
    0x136DD647,
    0xCAD731DC,
    0xC1D938D1,
    0xDCCB23C6,
    0xD7C52ACB,
    0xE6EF15E8,
    0xEDE11CE5,
    0xF0F307F2,
    0xFBFD0EFF,
    0x92A779B4,
    0x99A970B9,
    0x84BB6BAE,
    0x8FB562A3,
    0xBE9F5D80,
    0xB591548D,
    0xA8834F9A,
    0xA38D4697,
  ]
  U3 = [
    0x00000000,
    0x0D0B0E09,
    0x1A161C12,
    0x171D121B,
    0x342C3824,
    0x3927362D,
    0x2E3A2436,
    0x23312A3F,
    0x68587048,
    0x65537E41,
    0x724E6C5A,
    0x7F456253,
    0x5C74486C,
    0x517F4665,
    0x4662547E,
    0x4B695A77,
    0xD0B0E090,
    0xDDBBEE99,
    0xCAA6FC82,
    0xC7ADF28B,
    0xE49CD8B4,
    0xE997D6BD,
    0xFE8AC4A6,
    0xF381CAAF,
    0xB8E890D8,
    0xB5E39ED1,
    0xA2FE8CCA,
    0xAFF582C3,
    0x8CC4A8FC,
    0x81CFA6F5,
    0x96D2B4EE,
    0x9BD9BAE7,
    0xBB7BDB3B,
    0xB670D532,
    0xA16DC729,
    0xAC66C920,
    0x8F57E31F,
    0x825CED16,
    0x9541FF0D,
    0x984AF104,
    0xD323AB73,
    0xDE28A57A,
    0xC935B761,
    0xC43EB968,
    0xE70F9357,
    0xEA049D5E,
    0xFD198F45,
    0xF012814C,
    0x6BCB3BAB,
    0x66C035A2,
    0x71DD27B9,
    0x7CD629B0,
    0x5FE7038F,
    0x52EC0D86,
    0x45F11F9D,
    0x48FA1194,
    0x03934BE3,
    0x0E9845EA,
    0x198557F1,
    0x148E59F8,
    0x37BF73C7,
    0x3AB47DCE,
    0x2DA96FD5,
    0x20A261DC,
    0x6DF6AD76,
    0x60FDA37F,
    0x77E0B164,
    0x7AEBBF6D,
    0x59DA9552,
    0x54D19B5B,
    0x43CC8940,
    0x4EC78749,
    0x05AEDD3E,
    0x08A5D337,
    0x1FB8C12C,
    0x12B3CF25,
    0x3182E51A,
    0x3C89EB13,
    0x2B94F908,
    0x269FF701,
    0xBD464DE6,
    0xB04D43EF,
    0xA75051F4,
    0xAA5B5FFD,
    0x896A75C2,
    0x84617BCB,
    0x937C69D0,
    0x9E7767D9,
    0xD51E3DAE,
    0xD81533A7,
    0xCF0821BC,
    0xC2032FB5,
    0xE132058A,
    0xEC390B83,
    0xFB241998,
    0xF62F1791,
    0xD68D764D,
    0xDB867844,
    0xCC9B6A5F,
    0xC1906456,
    0xE2A14E69,
    0xEFAA4060,
    0xF8B7527B,
    0xF5BC5C72,
    0xBED50605,
    0xB3DE080C,
    0xA4C31A17,
    0xA9C8141E,
    0x8AF93E21,
    0x87F23028,
    0x90EF2233,
    0x9DE42C3A,
    0x063D96DD,
    0x0B3698D4,
    0x1C2B8ACF,
    0x112084C6,
    0x3211AEF9,
    0x3F1AA0F0,
    0x2807B2EB,
    0x250CBCE2,
    0x6E65E695,
    0x636EE89C,
    0x7473FA87,
    0x7978F48E,
    0x5A49DEB1,
    0x5742D0B8,
    0x405FC2A3,
    0x4D54CCAA,
    0xDAF741EC,
    0xD7FC4FE5,
    0xC0E15DFE,
    0xCDEA53F7,
    0xEEDB79C8,
    0xE3D077C1,
    0xF4CD65DA,
    0xF9C66BD3,
    0xB2AF31A4,
    0xBFA43FAD,
    0xA8B92DB6,
    0xA5B223BF,
    0x86830980,
    0x8B880789,
    0x9C951592,
    0x919E1B9B,
    0x0A47A17C,
    0x074CAF75,
    0x1051BD6E,
    0x1D5AB367,
    0x3E6B9958,
    0x33609751,
    0x247D854A,
    0x29768B43,
    0x621FD134,
    0x6F14DF3D,
    0x7809CD26,
    0x7502C32F,
    0x5633E910,
    0x5B38E719,
    0x4C25F502,
    0x412EFB0B,
    0x618C9AD7,
    0x6C8794DE,
    0x7B9A86C5,
    0x769188CC,
    0x55A0A2F3,
    0x58ABACFA,
    0x4FB6BEE1,
    0x42BDB0E8,
    0x09D4EA9F,
    0x04DFE496,
    0x13C2F68D,
    0x1EC9F884,
    0x3DF8D2BB,
    0x30F3DCB2,
    0x27EECEA9,
    0x2AE5C0A0,
    0xB13C7A47,
    0xBC37744E,
    0xAB2A6655,
    0xA621685C,
    0x85104263,
    0x881B4C6A,
    0x9F065E71,
    0x920D5078,
    0xD9640A0F,
    0xD46F0406,
    0xC372161D,
    0xCE791814,
    0xED48322B,
    0xE0433C22,
    0xF75E2E39,
    0xFA552030,
    0xB701EC9A,
    0xBA0AE293,
    0xAD17F088,
    0xA01CFE81,
    0x832DD4BE,
    0x8E26DAB7,
    0x993BC8AC,
    0x9430C6A5,
    0xDF599CD2,
    0xD25292DB,
    0xC54F80C0,
    0xC8448EC9,
    0xEB75A4F6,
    0xE67EAAFF,
    0xF163B8E4,
    0xFC68B6ED,
    0x67B10C0A,
    0x6ABA0203,
    0x7DA71018,
    0x70AC1E11,
    0x539D342E,
    0x5E963A27,
    0x498B283C,
    0x44802635,
    0x0FE97C42,
    0x02E2724B,
    0x15FF6050,
    0x18F46E59,
    0x3BC54466,
    0x36CE4A6F,
    0x21D35874,
    0x2CD8567D,
    0x0C7A37A1,
    0x017139A8,
    0x166C2BB3,
    0x1B6725BA,
    0x38560F85,
    0x355D018C,
    0x22401397,
    0x2F4B1D9E,
    0x642247E9,
    0x692949E0,
    0x7E345BFB,
    0x733F55F2,
    0x500E7FCD,
    0x5D0571C4,
    0x4A1863DF,
    0x47136DD6,
    0xDCCAD731,
    0xD1C1D938,
    0xC6DCCB23,
    0xCBD7C52A,
    0xE8E6EF15,
    0xE5EDE11C,
    0xF2F0F307,
    0xFFFBFD0E,
    0xB492A779,
    0xB999A970,
    0xAE84BB6B,
    0xA38FB562,
    0x80BE9F5D,
    0x8DB59154,
    0x9AA8834F,
    0x97A38D46,
  ]
  U4 = [
    0x00000000,
    0x090D0B0E,
    0x121A161C,
    0x1B171D12,
    0x24342C38,
    0x2D392736,
    0x362E3A24,
    0x3F23312A,
    0x48685870,
    0x4165537E,
    0x5A724E6C,
    0x537F4562,
    0x6C5C7448,
    0x65517F46,
    0x7E466254,
    0x774B695A,
    0x90D0B0E0,
    0x99DDBBEE,
    0x82CAA6FC,
    0x8BC7ADF2,
    0xB4E49CD8,
    0xBDE997D6,
    0xA6FE8AC4,
    0xAFF381CA,
    0xD8B8E890,
    0xD1B5E39E,
    0xCAA2FE8C,
    0xC3AFF582,
    0xFC8CC4A8,
    0xF581CFA6,
    0xEE96D2B4,
    0xE79BD9BA,
    0x3BBB7BDB,
    0x32B670D5,
    0x29A16DC7,
    0x20AC66C9,
    0x1F8F57E3,
    0x16825CED,
    0x0D9541FF,
    0x04984AF1,
    0x73D323AB,
    0x7ADE28A5,
    0x61C935B7,
    0x68C43EB9,
    0x57E70F93,
    0x5EEA049D,
    0x45FD198F,
    0x4CF01281,
    0xAB6BCB3B,
    0xA266C035,
    0xB971DD27,
    0xB07CD629,
    0x8F5FE703,
    0x8652EC0D,
    0x9D45F11F,
    0x9448FA11,
    0xE303934B,
    0xEA0E9845,
    0xF1198557,
    0xF8148E59,
    0xC737BF73,
    0xCE3AB47D,
    0xD52DA96F,
    0xDC20A261,
    0x766DF6AD,
    0x7F60FDA3,
    0x6477E0B1,
    0x6D7AEBBF,
    0x5259DA95,
    0x5B54D19B,
    0x4043CC89,
    0x494EC787,
    0x3E05AEDD,
    0x3708A5D3,
    0x2C1FB8C1,
    0x2512B3CF,
    0x1A3182E5,
    0x133C89EB,
    0x082B94F9,
    0x01269FF7,
    0xE6BD464D,
    0xEFB04D43,
    0xF4A75051,
    0xFDAA5B5F,
    0xC2896A75,
    0xCB84617B,
    0xD0937C69,
    0xD99E7767,
    0xAED51E3D,
    0xA7D81533,
    0xBCCF0821,
    0xB5C2032F,
    0x8AE13205,
    0x83EC390B,
    0x98FB2419,
    0x91F62F17,
    0x4DD68D76,
    0x44DB8678,
    0x5FCC9B6A,
    0x56C19064,
    0x69E2A14E,
    0x60EFAA40,
    0x7BF8B752,
    0x72F5BC5C,
    0x05BED506,
    0x0CB3DE08,
    0x17A4C31A,
    0x1EA9C814,
    0x218AF93E,
    0x2887F230,
    0x3390EF22,
    0x3A9DE42C,
    0xDD063D96,
    0xD40B3698,
    0xCF1C2B8A,
    0xC6112084,
    0xF93211AE,
    0xF03F1AA0,
    0xEB2807B2,
    0xE2250CBC,
    0x956E65E6,
    0x9C636EE8,
    0x877473FA,
    0x8E7978F4,
    0xB15A49DE,
    0xB85742D0,
    0xA3405FC2,
    0xAA4D54CC,
    0xECDAF741,
    0xE5D7FC4F,
    0xFEC0E15D,
    0xF7CDEA53,
    0xC8EEDB79,
    0xC1E3D077,
    0xDAF4CD65,
    0xD3F9C66B,
    0xA4B2AF31,
    0xADBFA43F,
    0xB6A8B92D,
    0xBFA5B223,
    0x80868309,
    0x898B8807,
    0x929C9515,
    0x9B919E1B,
    0x7C0A47A1,
    0x75074CAF,
    0x6E1051BD,
    0x671D5AB3,
    0x583E6B99,
    0x51336097,
    0x4A247D85,
    0x4329768B,
    0x34621FD1,
    0x3D6F14DF,
    0x267809CD,
    0x2F7502C3,
    0x105633E9,
    0x195B38E7,
    0x024C25F5,
    0x0B412EFB,
    0xD7618C9A,
    0xDE6C8794,
    0xC57B9A86,
    0xCC769188,
    0xF355A0A2,
    0xFA58ABAC,
    0xE14FB6BE,
    0xE842BDB0,
    0x9F09D4EA,
    0x9604DFE4,
    0x8D13C2F6,
    0x841EC9F8,
    0xBB3DF8D2,
    0xB230F3DC,
    0xA927EECE,
    0xA02AE5C0,
    0x47B13C7A,
    0x4EBC3774,
    0x55AB2A66,
    0x5CA62168,
    0x63851042,
    0x6A881B4C,
    0x719F065E,
    0x78920D50,
    0x0FD9640A,
    0x06D46F04,
    0x1DC37216,
    0x14CE7918,
    0x2BED4832,
    0x22E0433C,
    0x39F75E2E,
    0x30FA5520,
    0x9AB701EC,
    0x93BA0AE2,
    0x88AD17F0,
    0x81A01CFE,
    0xBE832DD4,
    0xB78E26DA,
    0xAC993BC8,
    0xA59430C6,
    0xD2DF599C,
    0xDBD25292,
    0xC0C54F80,
    0xC9C8448E,
    0xF6EB75A4,
    0xFFE67EAA,
    0xE4F163B8,
    0xEDFC68B6,
    0x0A67B10C,
    0x036ABA02,
    0x187DA710,
    0x1170AC1E,
    0x2E539D34,
    0x275E963A,
    0x3C498B28,
    0x35448026,
    0x420FE97C,
    0x4B02E272,
    0x5015FF60,
    0x5918F46E,
    0x663BC544,
    0x6F36CE4A,
    0x7421D358,
    0x7D2CD856,
    0xA10C7A37,
    0xA8017139,
    0xB3166C2B,
    0xBA1B6725,
    0x8538560F,
    0x8C355D01,
    0x97224013,
    0x9E2F4B1D,
    0xE9642247,
    0xE0692949,
    0xFB7E345B,
    0xF2733F55,
    0xCD500E7F,
    0xC45D0571,
    0xDF4A1863,
    0xD647136D,
    0x31DCCAD7,
    0x38D1C1D9,
    0x23C6DCCB,
    0x2ACBD7C5,
    0x15E8E6EF,
    0x1CE5EDE1,
    0x07F2F0F3,
    0x0EFFFBFD,
    0x79B492A7,
    0x70B999A9,
    0x6BAE84BB,
    0x62A38FB5,
    0x5D80BE9F,
    0x548DB591,
    0x4F9AA883,
    0x4697A38D,
  ]

  def __init__(self, key):
    if len(key) not in (16, 24, 32):
      raise ValueError("Invalid key size")

    rounds = self.number_of_rounds[len(key)]

    # Encryption round keys
    self._Ke = [[0] * 4 for i in range(rounds + 1)]

    # Decryption round keys
    self._Kd = [[0] * 4 for i in range(rounds + 1)]

    round_key_count = (rounds + 1) * 4
    KC = len(key) // 4

    # Convert the key into ints
    tk = [struct.unpack(">i", key[i : i + 4])[0] for i in range(0, len(key), 4)]

    # Copy values into round key arrays
    for i in range(0, KC):
      self._Ke[i // 4][i % 4] = tk[i]
      self._Kd[rounds - (i // 4)][i % 4] = tk[i]

    # Key expansion (fips-197 section 5.2)
    rconpointer = 0
    t = KC
    while t < round_key_count:
      tt = tk[KC - 1]
      tk[0] ^= (
        (self.S[(tt >> 16) & 0xFF] << 24)
        ^ (self.S[(tt >> 8) & 0xFF] << 16)
        ^ (self.S[tt & 0xFF] << 8)
        ^ self.S[(tt >> 24) & 0xFF]
        ^ (self.rcon[rconpointer] << 24)
      )
      rconpointer += 1

      if KC != 8:
        for i in range(1, KC):
          tk[i] ^= tk[i - 1]

      # Key expansion for 256-bit keys is "slightly different" (fips-197)
      else:
        for i in range(1, KC // 2):
          tk[i] ^= tk[i - 1]
        tt = tk[KC // 2 - 1]

        tk[KC // 2] ^= (
          self.S[tt & 0xFF]
          ^ (self.S[(tt >> 8) & 0xFF] << 8)
          ^ (self.S[(tt >> 16) & 0xFF] << 16)
          ^ (self.S[(tt >> 24) & 0xFF] << 24)
        )

        for i in range(KC // 2 + 1, KC):
          tk[i] ^= tk[i - 1]

      # Copy values into round key arrays
      j = 0
      while j < KC and t < round_key_count:
        self._Ke[t // 4][t % 4] = tk[j]
        self._Kd[rounds - (t // 4)][t % 4] = tk[j]
        j += 1
        t += 1

    # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3)
    for r in range(1, rounds):
      for j in range(0, 4):
        tt = self._Kd[r][j]
        self._Kd[r][j] = (
          self.U1[(tt >> 24) & 0xFF]
          ^ self.U2[(tt >> 16) & 0xFF]
          ^ self.U3[(tt >> 8) & 0xFF]
          ^ self.U4[tt & 0xFF]
        )

  def encrypt(self, plaintext):
    "Encrypt a block of plain text using the AES block cipher."

    if len(plaintext) != 16:
      raise ValueError("wrong block length")

    rounds = len(self._Ke) - 1
    (s1, s2, s3) = [1, 2, 3]
    a = [0, 0, 0, 0]

    # Convert plaintext to (ints ^ key)
    t = [
      (_compact_word(plaintext[4 * i : 4 * i + 4]) ^ self._Ke[0][i])
      for i in range(0, 4)
    ]

    # Apply round transforms
    for r in range(1, rounds):
      for i in range(0, 4):
        a[i] = (
          self.T1[(t[i] >> 24) & 0xFF]
          ^ self.T2[(t[(i + s1) % 4] >> 16) & 0xFF]
          ^ self.T3[(t[(i + s2) % 4] >> 8) & 0xFF]
          ^ self.T4[t[(i + s3) % 4] & 0xFF]
          ^ self._Ke[r][i]
        )
      t = copy.copy(a)

    # The last round is special
    result = []
    for i in range(0, 4):
      tt = self._Ke[rounds][i]
      result.append((self.S[(t[i] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
      result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
      result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
      result.append((self.S[t[(i + s3) % 4] & 0xFF] ^ tt) & 0xFF)

    return result

  def decrypt(self, ciphertext):
    "Decrypt a block of cipher text using the AES block cipher."

    if len(ciphertext) != 16:
      raise ValueError("wrong block length")

    rounds = len(self._Kd) - 1
    (s1, s2, s3) = [3, 2, 1]
    a = [0, 0, 0, 0]

    # Convert ciphertext to (ints ^ key)
    t = [
      (_compact_word(ciphertext[4 * i : 4 * i + 4]) ^ self._Kd[0][i])
      for i in range(0, 4)
    ]

    # Apply round transforms
    for r in range(1, rounds):
      for i in range(0, 4):
        a[i] = (
          self.T5[(t[i] >> 24) & 0xFF]
          ^ self.T6[(t[(i + s1) % 4] >> 16) & 0xFF]
          ^ self.T7[(t[(i + s2) % 4] >> 8) & 0xFF]
          ^ self.T8[t[(i + s3) % 4] & 0xFF]
          ^ self._Kd[r][i]
        )
      t = copy.copy(a)

    # The last round is special
    result = []
    for i in range(0, 4):
      tt = self._Kd[rounds][i]
      result.append((self.Si[(t[i] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
      result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
      result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
      result.append((self.Si[t[(i + s3) % 4] & 0xFF] ^ tt) & 0xFF)

    return result


class Counter(object):
  """A counter object for the Counter (CTR) mode of operation.

  To create a custom counter, you can usually just override the
  increment method."""

  def __init__(self, initial_value=1):
    # Convert the value into an array of bytes long
    self._counter = [((initial_value >> i) % 256) for i in range(128 - 8, -1, -8)]

  value = property(lambda s: s._counter)

  def increment(self):
    """Increment the counter (overflow rolls back to 0)."""

    for i in range(len(self._counter) - 1, -1, -1):
      self._counter[i] += 1

      if self._counter[i] < 256:
        break

      # Carry the one
      self._counter[i] = 0

    # Overflow
    else:
      self._counter = [0] * len(self._counter)


class AESBlockModeOfOperation(object):
  """Super-class for AES modes of operation that require blocks."""

  def __init__(self, key):
    self._aes = AES(key)

  def decrypt(self, ciphertext):
    raise Exception("not implemented")

  def encrypt(self, plaintext):
    raise Exception("not implemented")


class AESStreamModeOfOperation(AESBlockModeOfOperation):
  """Super-class for AES modes of operation that are stream-ciphers."""


class AESSegmentModeOfOperation(AESStreamModeOfOperation):
  """Super-class for AES modes of operation that segment data."""

  segment_bytes = 16


class AESModeOfOperationECB(AESBlockModeOfOperation):
  """AES Electronic Codebook Mode of Operation.

      o Block-cipher, so data must be padded to 16 byte boundaries

  Security Notes:
      o This mode is not recommended
      o Any two identical blocks produce identical encrypted values,
        exposing data patterns. (See the image of Tux on wikipedia)

  Also see:
      o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29
      o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1"""

  name = "Electronic Codebook (ECB)"

  def encrypt(self, plaintext):
    if len(plaintext) != 16:
      raise ValueError("plaintext block must be 16 bytes")

    plaintext = _string_to_bytes(plaintext)
    return _bytes_to_string(self._aes.encrypt(plaintext))

  def decrypt(self, ciphertext):
    if len(ciphertext) != 16:
      raise ValueError("ciphertext block must be 16 bytes")

    ciphertext = _string_to_bytes(ciphertext)
    return _bytes_to_string(self._aes.decrypt(ciphertext))


class AESModeOfOperationCBC(AESBlockModeOfOperation):
  """AES Cipher-Block Chaining Mode of Operation.

      o The Initialization Vector (IV)
      o Block-cipher, so data must be padded to 16 byte boundaries
      o An incorrect initialization vector will only cause the first
        block to be corrupt; all other blocks will be intact
      o A corrupt bit in the cipher text will cause a block to be
        corrupted, and the next block to be inverted, but all other
        blocks will be intact.

  Security Notes:
      o This method (and CTR) ARE recommended.

  Also see:
      o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29
      o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2"""

  name = "Cipher-Block Chaining (CBC)"

  def __init__(self, key, iv=None):
    if iv is None:
      self._last_cipherblock = [0] * 16
    elif len(iv) != 16:
      raise ValueError("initialization vector must be 16 bytes")
    else:
      self._last_cipherblock = _string_to_bytes(iv)

    AESBlockModeOfOperation.__init__(self, key)

  def encrypt(self, plaintext):
    if len(plaintext) != 16:
      raise ValueError("plaintext block must be 16 bytes")

    plaintext = _string_to_bytes(plaintext)
    precipherblock = [(p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock)]
    self._last_cipherblock = self._aes.encrypt(precipherblock)

    return _bytes_to_string(self._last_cipherblock)

  def decrypt(self, ciphertext):
    if len(ciphertext) != 16:
      raise ValueError("ciphertext block must be 16 bytes")

    cipherblock = _string_to_bytes(ciphertext)
    plaintext = [
      (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock)
    ]
    self._last_cipherblock = cipherblock

    return _bytes_to_string(plaintext)


class AESModeOfOperationCFB(AESSegmentModeOfOperation):
  """AES Cipher Feedback Mode of Operation.

     o A stream-cipher, so input does not need to be padded to blocks,
       but does need to be padded to segment_size

  Also see:
     o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29
     o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3"""

  name = "Cipher Feedback (CFB)"

  def __init__(self, key, iv, segment_size=1):
    if segment_size == 0:
      segment_size = 1

    if iv is None:
      self._shift_register = [0] * 16
    elif len(iv) != 16:
      raise ValueError("initialization vector must be 16 bytes")
    else:
      self._shift_register = _string_to_bytes(iv)

    self._segment_bytes = segment_size

    AESBlockModeOfOperation.__init__(self, key)

  segment_bytes = property(lambda s: s._segment_bytes)

  def encrypt(self, plaintext):
    if len(plaintext) % self._segment_bytes != 0:
      raise ValueError("plaintext block must be a multiple of segment_size")

    plaintext = _string_to_bytes(plaintext)

    # Break block into segments
    encrypted = []
    for i in range(0, len(plaintext), self._segment_bytes):
      plaintext_segment = plaintext[i : i + self._segment_bytes]
      xor_segment = self._aes.encrypt(self._shift_register)[: len(plaintext_segment)]
      cipher_segment = [(p ^ x) for (p, x) in zip(plaintext_segment, xor_segment)]

      # Shift the top bits out and the ciphertext in
      self._shift_register = _concat_list(
        self._shift_register[len(cipher_segment) :], cipher_segment
      )

      encrypted.extend(cipher_segment)

    return _bytes_to_string(encrypted)

  def decrypt(self, ciphertext):
    if len(ciphertext) % self._segment_bytes != 0:
      raise ValueError("ciphertext block must be a multiple of segment_size")

    ciphertext = _string_to_bytes(ciphertext)

    # Break block into segments
    decrypted = []
    for i in range(0, len(ciphertext), self._segment_bytes):
      cipher_segment = ciphertext[i : i + self._segment_bytes]
      xor_segment = self._aes.encrypt(self._shift_register)[: len(cipher_segment)]
      plaintext_segment = [(p ^ x) for (p, x) in zip(cipher_segment, xor_segment)]

      # Shift the top bits out and the ciphertext in
      self._shift_register = _concat_list(
        self._shift_register[len(cipher_segment) :], cipher_segment
      )

      decrypted.extend(plaintext_segment)

    return _bytes_to_string(decrypted)


class AESModeOfOperationOFB(AESStreamModeOfOperation):
  """AES Output Feedback Mode of Operation.

     o A stream-cipher, so input does not need to be padded to blocks,
       allowing arbitrary length data.
     o A bit twiddled in the cipher text, twiddles the same bit in the
       same bit in the plain text, which can be useful for error
       correction techniques.

  Also see:
     o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29
     o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4"""

  name = "Output Feedback (OFB)"

  def __init__(self, key, iv=None):
    if iv is None:
      self._last_precipherblock = [0] * 16
    elif len(iv) != 16:
      raise ValueError("initialization vector must be 16 bytes")
    else:
      self._last_precipherblock = _string_to_bytes(iv)

    self._remaining_block = []

    AESBlockModeOfOperation.__init__(self, key)

  def encrypt(self, plaintext):
    encrypted = []
    for p in _string_to_bytes(plaintext):
      if len(self._remaining_block) == 0:
        self._remaining_block = self._aes.encrypt(self._last_precipherblock)
        self._last_precipherblock = []
      precipherbyte = self._remaining_block.pop(0)
      self._last_precipherblock.append(precipherbyte)
      cipherbyte = p ^ precipherbyte
      encrypted.append(cipherbyte)

    return _bytes_to_string(encrypted)

  def decrypt(self, ciphertext):
    # AES-OFB is symetric
    return self.encrypt(ciphertext)


class AESModeOfOperationCTR(AESStreamModeOfOperation):
  """AES Counter Mode of Operation.

      o A stream-cipher, so input does not need to be padded to blocks,
        allowing arbitrary length data.
      o The counter must be the same size as the key size (ie. len(key))
      o Each block independant of the other, so a corrupt byte will not
        damage future blocks.
      o Each block has a uniue counter value associated with it, which
        contributes to the encrypted value, so no data patterns are
        leaked.
      o Also known as: Counter Mode (CM), Integer Counter Mode (ICM) and
        Segmented Integer Counter (SIC

  Security Notes:
      o This method (and CBC) ARE recommended.
      o Each message block is associated with a counter value which must be
        unique for ALL messages with the same key. Otherwise security may be
        compromised.

   Also see:

      o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29
      o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.5
        and Appendix B for managing the initial counter"""

  name = "Counter (CTR)"

  def __init__(self, key, counter=None):
    AESBlockModeOfOperation.__init__(self, key)

    if counter is None:
      counter = Counter()

    self._counter = counter
    self._remaining_counter = []

  def encrypt(self, plaintext):
    while len(self._remaining_counter) < len(plaintext):
      self._remaining_counter += self._aes.encrypt(self._counter.value)
      self._counter.increment()

    plaintext = _string_to_bytes(plaintext)

    encrypted = [(p ^ c) for (p, c) in zip(plaintext, self._remaining_counter)]
    self._remaining_counter = self._remaining_counter[len(encrypted) :]

    return _bytes_to_string(encrypted)

  def decrypt(self, crypttext):
    # AES-CTR is symetric
    return self.encrypt(crypttext)


# Simple lookup table for each mode
AESModesOfOperation = dict(
  ctr=AESModeOfOperationCTR,
  cbc=AESModeOfOperationCBC,
  cfb=AESModeOfOperationCFB,
  ecb=AESModeOfOperationECB,
  ofb=AESModeOfOperationOFB,
)
